Using Browserify in the app

Until now we have learned about what Browserify is and how to use it. Now we will apply that background to our contacts app to load all the code as Node modules.

Before we continue, ensure that you have installed all the required dependencies for the project:

{
  "name": "mastering-backbone-04",
  // ...
  "dependencies": {
    "backbone": "^1.2.3",
    "backbone-validation": "^0.11.5",
    "body-parser": "^1.14.1",
    "bootstrap": "^3.3.5",
    "browser-sync": "^2.9.11",
    "crispy-string": "0.0.2",
    "express": "^4.13.3",
    "http-proxy": "^1.11.3",
    "jquery": "^2.1.4",
    "lodash": "^3.10.1",
    "morgan": "^1.6.1",
    "sweetalert": "^1.1.3",
    "underscore": "^1.8.3"
  }
}

The easiest modules to convert are Models and Collections because they don't have huge dependencies.

// apps/contacts/models/contact.js
'use strict'

var Backbone = require('backbone');

class Contact extends Backbone.Model {
  // ...
}

module.exports = Contact;

As you can see, the module remains almost the same. We have just added the require() calls and the export statement at the end of the file:

// apps/contacts/contactCollection.js
'use strict';

var Backbone = require('backbone');
var Contact = require('../models/contact');

class ContactCollection extends Backbone.Collection {
  // ...

  get model() {
return Contact;
  }
}

module.exports = ContactCollection;

Views are easy to convert, too. We just have to add the require() calls as we did with the Contact and ContactCollection modules. Before we continue, we need an extra step with the views; currently all the views for a given controller are contained in a single script; contactEditor.js for example contains ContactForm, ContactPreview, and PhoneList, and so on.

As we are modularizing the project, it's a good idea to put each view in its own file and require it when we need it. The following shows this idea. You have many good reasons to do this: to isolate your components for testing, to keep your files small, to improve maintenance, and to get interchangeable modules.

Using Browserify in the app

Figure 4.3 Splitting views to their own files

'use strict';

var Layout = require('../../common').Layout;

class ContactFormLayout extends Layout {
// ...
}

module.exports = ContactFormLayout;

As you can see, the conversion of plain JavaScript files to Node modules is very easy to do. The code of the business logic is exactly the same. The subapplication controller depends on the views that we have converted on the previous step.

'use strict';

var _ = require('underscore');
var Backbone = require('backbone');
var App = require('../../app');
var ContactFormLayout = require('./views/contactFormLayout');
var ContactPreview = require('./views/contactPreview');
var PhoneListView = require('./views/phoneListView');
var EmailListView = require('./views/emailListView');
var ContactForm = require('./views/contactForm');
var PhoneCollection = require('./collections/phoneCollection');
var EmailCollection = require('./collections/emailCollection');

class ContactEditor {
  // ...
}

module.exports = ContactEditor;

The application façade depends on many subapplication controllers, the models, and the collections:

'use strict';

var App = require('../../app');
var ContactList = require('./contactList');
var ContactViewer = require('./contactViewer');
var ContactEditor = require('./contactEditor');
var Contact = require('./models/contact');
var ContactCollection = require('./collections/contactCollection');

function ContactsApp(options) {
  // ...
}

// ...

module.exports = ContactsApp;

The routers depend on the subapplication façade and the application infrastructure:

'use strict';

var Backbone = require('backbone');
var App = require('../../app');
var ContactsApp = require('./app');

var ContactsRouter = Backbone.Router.extend({
  // ...
});

module.exports = new ContactsRouter();

The App object is responsible for loading all the subapplication routers and then starting the history module:

'use strict';

var _ = require('underscore');
var Backbone = require('backbone');
var BackboneValidation = require('backbone-validation');
var swal = require('sweetalert');
var noty = require('noty');
var Region = require('./common').Region;

// Initialize all available routes
require('./apps/contacts/router');

var App = {
start() {
    // The common place where sub-applications will be showed
    App.mainRegion = new Region({el: '#main'});

    // Create a global router to enable sub-applications to
    // redirect to other URLs
    App.router = new DefaultRouter();
    Backbone.history.start();
  },

// ...
};

// ...

module.exports = App;

The next step is to start the application by calling the start() method on the App object; this is done from the index.html file:

<script type="text/javascript">App.start();</script>

As we are re-packing the application with Browserify, it's better to create a new file to the main entry point:

// main.js
var App = require('./app');

App.start();

Once our application is written as Node modules, we can use Browserify to bundle the code in a single script:

$ mkdir –p .tmp/js
$ cd app/js
$ browserify main.js -o ../../.tmp/js/app.js

This will create a bundled file with all the dependencies on it. To use the bundled version of the code, we have to change the index.htm file to load it instead of loading all the individual files:

<html>
<head>
    // ...
</head>
<body>
// ...
<script src="js/app.js"></script>
</body>
</html>

That should be enough; however, the application won't start because we have a cyclic dependency issue.

Solving cyclic dependency

Having two modules that depend on each other is called cyclic dependency. In our Contacts application, the infrastructure application depends on the subapplication routers, and the routers depend on the application infrastructure to load the subapplication controllers and facades. Figure 4.4 shows how this looks.

Solving cyclic dependency

Figure 4.4 Cyclic dependencies

It is not possible to run the application properly because of the cyclic dependency. Here is what happens in detail.

  • The App module is executed
  • The App requires ContactsRouter:
    var ContactsRouter = require('./apps/contacts/router');
  • ContactsRouter requires the App module but the App module is not exported yet:
    var App = require('../../app'); // returns undefined
  • ContactsRouter receives an undefined value for the App variable
  • The App module continue the execution and finally exposes the App value:
    var App = {
      // ...
    };
    
    module.exports = App;
  • ContactsRouter matches a route, but as the App value is undefined it triggers an error:
    startApp() {
      // App = undefined
      return App.startSubApplication(ContactsApp);
    }

We should break the cycle in some way. An easy approach to do it is to require the App module after it is exported. Instead of requiring the App module from ContactsRouter on top of the file, we can do it only when it's necessary:

// apps/contacts/router.js
class ContactsRouter extends Backbone.Router {
  // ...

  startApp() {
    var App = require('../../app');
    var ContactsApp = require('./app');
    return App.startSubApplication(ContactsApp);
  }
}

This is a simple but effective way to break a cyclic dependency. Now you can re-bundle the application and run the application again. It should work:

$ mkdir –p .tmp/js
$ cd app/js
$ browserify main.js -o ../../.tmp/js/app.js
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.212.124