Mostrando postagens com marcador mobile. Mostrar todas as postagens
Mostrando postagens com marcador mobile. Mostrar todas as postagens

sábado, 10 de outubro de 2015

In the way to a scalable application with Marionette

One of lessons learned while building my first javascript mobile application was the need to use a framework on top of Backbone. Marionette is the natural choice given it’s popularity and active development.

I bought, and read, David Sulc’s introductory book for Marionette. It’s a great resource to get in touch with the framework but one of the topics that falls short is the structure, based in modules, a deprecated feature and discarded even by it’s creator. There’s a follow up book that explains how to use RequireJS with it.

After writing a working example i felt the next step was build a full application, even if small. It should use a sane architecture to serve as base for future larger applications.

The search for reference applications

I remembered that the Marionette homepage has links to some example applications so i looked into them for possible starting points. Edit was discarded because of CoffeScript, Streamus seems tied to Chrome extension. Both Gistbook and Marionette Wires impressed me by its modular, organized architecture. In the down side they use specialized components (router) and Babel/ES6 and i don’t want, at my current stage, to learn so many new technologies/tools at same time.

As a last resort, i evaluated TodoMVC. A quick look at the code showed that is not what i was looking for. It uses global variables (although well organized into a namespace) and lacks a few common iterations like page transitions.

Kicking off

Since none of the example applications could be used as starting point, i setup the project from scratch. After the usual installation of packages and configuration of module loader (Webpack), i got stuck at the application bootstrap/start.

In the Application section of the Marionette documentation, the Application Regions feature is marked as deprecated and suggest to use a root LayoutView, something like:

var RootView = Marionette.LayoutView.extend({
  el: 'body'
});

//followed by

app.rootView = new RootView();

I did something similar…

var RootView = Marionette.LayoutView.extend({
  el: 'body',
  regions: {
    'header': '#app-header',
    'content': '#app-content'
  }
});

var Application = Marionette.Application.extend({
  initialize: function () {
    this.rootView = new RootView({});
  },
  onStart: function() {
    console.log('app start');
  }

});

//followed by

var app = new Application();
app.start();
app.rootView.showChildView('content',new MyView());

… and got an error message:

Error: An “el” #app-content must exist in DOM

After a while i figured that i have to put the script at the end of html file or wrap the initialization code in $.ready handler. It took some time to show the first view, but is rewarding anyway.

Decouple roads

To write a scalable application is necessary a mechanism to navigate through it. While a basic requirement is also a hard one to implement in a decoupled, flexible and uniform way. Below a some patterns i found in the above examples:

  • Execute a method or trigger an event in the app instance. The example of A Gentle Introduction book does this. But in a not uniform way.
    Sometimes triggers an event…
contactsListView.on("childview:contact:show", function(childView, args){
   ContactManager.trigger("contact:show", args.model.get("id"));
});

…sometimes updates the application regions directly.

contactsListView.on("childview:contact:edit", function(childView, args){
   var model = args.model;
   var view = new ContactManager.ContactsApp.Edit.Contact({
     model: model
   });
   //...

  ContactManager.regions.dialog.show(view);
});

This pattern has the drawback of tying to the application instance and interface.

  • Use hyperlinks and let the router do the actual work. This approach is used in Marionette Wires example:
  //a  book route is defined
  routes: {
    'books/:id' : 'show'
  },

  show() {
    return new ShowRoute({
      layout: this.layout
    });
  }
  
  //in the view a hyperlink is created for each book 
  export default ItemView.extend({
    template: template,
    tagName: 'a',
    attributes() {
      return {
        href  : `#books/${this.model.get('id')}`
      };
    }
  });

This pattern has some criticism that may not be the case in the specific app since it uses an customized route to take care of state. Anyway it requires a pretty opinionated architecture which is no-go for me at least for now.

  • Uses a pub/sub mechanism. Gistbook uses Radio to request a view to be show. Pretty simple and straightforward:
  //configure the handler 
  var RootView = LayoutView.extend({
  initialize() {
    var containerRegion = this.getRegion('container');
    Radio.comply('rootView', 'showIn:container', containerRegion.show, containerRegion);
  }
});
 //later do the request
  export default Route.extend({
   show() {
    var gistView = new GistView({
      model: new Gist(),
      ownGistbook: false,
      homePage: true
    });
    Radio.command('rootView', 'showIn:container', gistView);
 }
});

Gistbook also uses an specialized router to take care of state. In fact i could not get the full picture of how it works. Anyway, using pub/sub pattern seems a good idea even if used without a state router.

With base structure done, is time to some coding. Let’s see results.

sábado, 15 de agosto de 2015

CAM-ICU: the first mobile web app of a desktop developer

CAM-ICU is a tool used to assess delirium development  in critical care patients. When a friend of mine asked for an application to use in his Intensive Care Unit, i decided to write it as mobile web app. It was the perfect fit to learn how to program with HTML and Javascript: an application that has actual usage / challenges, unlike most demos out there, but not as big that would make unpractical to develop.

As a long time desktop developer, starting with web development, i faced a bunch of issues developing the app. More than a guide to create an app this post aims to record the development process, how i overcome the found problems and what i learned for my future web development projects.

The source code can be found here and a live demo here

General application structure


The application was built using Backbone with some plugins (StickIt, Validation, Computed). Bootstrap was used as the CSS framework, Handlebars as template engine and JQuery for DOM manipulation. The Backbone Yeoman generator was used to scaffold and create models and views. The backend is a CGI application developed with Freepascal and Lazarus that implements a REST service.

The (not so) good old Backbone


As already explained, i choose to structure the app using Backbone. It forces me to learn the basics of HTML/DOM/JS unlike Ember or Angular that abstracts them with a lot of "magic" behind the scenes. Other Backbone advantage is its usage pattern which is similar to what i already do in desktop.

But...

Having to write boilerplate code like render in View "classes" is annoying and error prone. To avoid memory leaks is necessary to keep track of child views, another bothersome step. The natural step was to create base classes handling these problems but than i would recreate the wheel.

    render: function () {
      this.$el.html(this.html);
      this.stickit(this.model);
      return this;
    }
You get tired of writing such code early

On the other hand, Backbone's Model/Collection/Events layer is at the same time easy to grasp and powerful, providing the elements to structure the code in a organized, maintainable way.

The bless and curse of Backbone plugins


As soon as the newbie Backbone developer try to learn more about the library, he/she is confronted with tons of plugins adding tons of features on top of the bare components.

This is a good thing because allows the programmer to pick the extensions that provides the needed features and better adapt to his/her work flow. But, given the great number of choices, is necessary to spend some time evaluating those libraries. As an example, i needed to extend Backbone.Model with computed fields. I found four plugins: Mutators, compute, computed-properties and computedfields. Mutators is complete and well maintained but too intrusive for my taste, compute seems dead, computed-properties misses a needed feature (toJSON handling) and patches Backbone.Model constructor, finally computedfields was the choose one because of its good balance of feature and light weightiness.

Can use it !== must use it


Not necessarily related to Backbone or web development, but having so many features/extensions at disposal makes easy to overuse or misuse them. I felt in this temptation. In the code below i registered a computed field dependent of an property instantiated at demand (_evaluations). It store the callback function in an property just to be called later.

evaluationcount: {
 get: function () {
   if (this._evaluations) {
   return this._evaluations.length;
   } else {
   return this.get('evaluationcount')
    }
},

 depends: [
 function (callback) {
    this._updateEvaluationCount = callback;
   }],
   toJSON: false}

[..]

evaluations.fetch({
  success: function (collection) {
    self._evaluations = evaluations;
    self._updateEvaluationCount();  
    evaluations.on('add remove reset', self._updateEvaluationCount);
  }
})
Instead of using the computed plugin, i could use the standard Backbone primitives to accomplish the same in a cleaner, smaller way:

   
evaluations.fetch({
   success: function (collection) {
     self._evaluations = evaluations;
     evaluations.on('add remove reset', function(collection){
    self.set('evaluationcount', collection.length);
     });
   }
})
A lot better.

Bootstrap for mobile web applications



To style the application, the natural choice would be a framework like jQuery Mobile but this would mean the need to learn yet another technology. Since Bootstrap has first class mobile support and i already knew its basics, it was the selected.

I learned the hard way that Bootstrap is tailored to mobile pages not mobile apps. Many of the user iterations and common designs of mobile apps cannot be accomplished with a vanilla Bootstrap. The result is the need to change a few behaviors leading to a subpar developer and user experience. MobileAngularUI adapts Bootstrap to have a look and feel better suited to apps. Unfortunately is incomplete and development has been slow.

Yo fail!


In the web programming ecosystem there are a lot of tools aimed to ease the application development process. And every day new tools arrive. This makes hard, specially for newcomers, to learn how to setup a productive environment.

Yeoman is presented as a tool to address this issue by making easy setup best practice development environments ready for consumption. Unfortunately, i hit some issues while using it. I could not get the optimized requirejs build to work out of the box. I had to manually adjust the configuration and even than did not work. After some more tries, i gave up and finalized the build manually. The non official Bootstrap Sass port was discontinued and i had figure myself. No luck making livereload work, had to try browser-sync with no better outcome.

Another problem is that with the myriad of options is hard to find a generator that fits the developer specific needs. I decided to go with MarionetteJs, Webpack, Gulp, FlightJs, Bootstrap for my next projects but could not find a generator that produces such environment. To keep using Yeoman i would need to learn how to create a generator and how to configure the required tools. No thanks. 

Learnings


All in all, it was a rewarding and funny experience. The problems i faced were not insurmountable and should be considered natural stumbles of the learning process. In the end, i've got some lessons:

  • A Backbone extension like Marionette, Chaplin and LayoutManager should be used to bigger than demos apps. Marionette seems to be the one with more traction
  • Plugins are great but must be carefully evaluated to avoid the poor coded ones. A good thing also is to use the plugins only in the really needed cases.
  • Bootstrap is not suited for mobile web apps. jQuery Mobile, Kendo Mobile, ChocolateChipUI, Framework7 or even Ionic css are the real deal.
  • The current JS client application develop/build experience falls short. Lot of options and rapidly changing build tools is a barrier for newcomers. Yeoman is not the right response for this problem.