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

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
Made with Slides.com