Coming Up in Agent
Major Front-end Refactor
- All Javascript, no Less
- Code has been moved around, syntax updated
- Functionality is the same
- A different customization path
Why Change?
- Use the latest Javscript syntax and features
- Split the code up for easier unit testing
- Better support for non-invasive customization
- Easier merging of new versions
Latest JS Features
var self = this;
this.model.save({
success: function() {
self.callMethod();
},
});
this.model.save({
success: () => this.callMethod(),
});
Arrow Functions
Latest JS Features
define([
'app',
'dependency1',
'dependency2', // Notice not used
'template1',
], function (app, dependency1, dependency2, template1) {
//...
dependency1.callMethod();
app.vent.trigger('myEvent');
// render template1
//...
return new MyController();
});
import app from 'app';
import dependency1 from 'dependency1';
import dependency2 from 'dependency2', // Throws an eslint error now
import template1 from 'template1';
//...
dependency1.callMethod();
app.vent.trigger('myEvent');
// render template1
//...
export default new MyController();
Import/Export
Latest JS Features
const MyController = Marionette.Object.extend({
show: function (region) {
const model = new MyModel();
const view = new MyView({ model: model });
region.show(view);
},
});
const MyController = Marionette.Object.extend({
show(region) {
const model = new MyModel();
const view = new MyView({ model });
region.show(view);
},
});
Object Shorthand Notation
Latest JS Features
Classes
class MyController extends Marionette.Object {
show(region) {
const model = new MyModel();
const view = new MyView({ model });
region.show(view);
}
}
const MyController = Marionette.Object.extend({
show: function (region) {
const model = new MyModel();
const view = new MyView({ model: model });
region.show(view);
},
});
Latest JS Features
Other Resources
- Egghead.io Video Tutorial
- Treehouse Tutorial
Code Splitting
// MyModel.js
const MyModel = // ...
export default MyModel;
// MyView.js
const MyView = // ...
export default MyView;
// MyController.js
const MyController = //...
export default MyController;
// Direct access to all exports
// MyFeature.js
const MyModel = // ...
const MyView = // ...
const MyController = //...
return new MyController();
// No direct access to MyModel or MyView
Testability
Code Splitting
Customization
// core/MyModel.js
const MyModel = Backbone.Model.extend({
parse(res) {
return res.data;
},
});
export default MyModel;
// custom/MyModel.js
import MyModel from 'MyModel';
const MyCustomModel = MyModel.extend({
parse(res) {
return res;
},
});
export default MyCustomModel;
More about how to do this later
What To Do
- Move all custom files to 'custom' bottle
- Identify changed baseline files
- Merge baseline and look for conflicts
- Move customizations to a separate file under 'custom' bottle, extending components as needed
- Last pass: Delete 'Agent.Shared.Core', 'Agent.Console', and 'Agent.Support' script directories and copy baseline ones in
- Look for a changeset, any file that changed means it was customized
What To Do
How to extend a baseline component
// source/Agent.Shared.Core/content/scripts/app/core/BaseModule.js
class BaseModule {
show(region) {
const view = new MyView();
region.show(view);
}
}
export default BaseModule;
// source/custom/content/scripts/app/core/BaseModule.js
import BaseModule from 'override/core/BaseModule';
class CustomModule extends BaseModule {
show(region) {
// Don't have to call this
super.show(region);
// do more here
}
}
export default CustomModule;
What To Do
How to replace a baseline component
// source/Agent.Shared.Core/content/scripts/app/core/BaseModule.js
class BaseModule {
show(region) {
const view = new MyView();
region.show(view);
}
}
export default BaseModule;
// source/custom/content/scripts/app/core/BaseModule.js
class CustomModule {
show(region) {
// do what you want
}
}
export default CustomModule;
What To Do
How to extend a baseline view
// source/Agent.Shared.Core/content/scripts/app/core/BaseView.js
const BaseView = Marionette.ItemView.extend({
template: baseTemplate,
events: {
'click .element': 'execute',
},
onRender() {
// stuff
},
execute(e) {
// stuff
},
});
export default BaseView;
// source/custom/content/scripts/app/core/BaseView.js
import BaseView from 'override/core/BaseView';
const CustomView = BaseView.extend({
events: _.extend({}, BaseView.prototype.events, {
'click .another-element': 'executeElse',
},
onRender() {
BaseView.prototype.onRender.apply(this, arguments);
// stuff
},
executeElse(e) {
// stuff
},
});
export default CustomView;
What To Do
Common themes in new code
- Tab settings are created in `generateTabs` method
- Easy to extend (OrderedMap)
- Data for forms are built in `getData` method
showTabs() {
const tabSettings = this.generateTabs();
this.tabComponent.show(region, tabSettings.flatten());
}
generateTabs() {
const tabs = new OrderedMap();
tabs.add('tabOne', { /* settings */ });
tabs.add('tabTwo', { /* settings */ });
return tabs;
}
generateTabs() {
const tabs = Base.prototype.generateTabs.apply(this, arguments);
tabs.add('tabThree', { /* settings */ });
tabs.remove('tabOne');
tabs.insert('anotherTab', { /* settings */ }, 1);
return tabs;
}
Common Gotchas
- Extend the prototype of the parent, not just the parent
- Cannot extend a template, only replace it
- If customizing a singleton (contains a 'getInstance' static method, be sure to override that method so that it creates your custom class
Conclusion
- Moving custom code to the custom bottle will allow for smoother upgrades
- Don't change baseline files, extend them instead
- Read dev release notes
- We will strive to document API changes so that adjusting customizations is easy
- Give feedback
- Are there methods that can be split more granularly for simpler customization
Coming Up in Agent
By Craig Jennings
Coming Up in Agent
- 1,071