Introduction
I think it is no secret to anyone that client applications in modern web services are becoming more complex and the number of JS code in them is growing. Until recently, the client-side architecture was usually developed from scratch and was specific for each project. It is not surprising that we had to face typical tasks again and again.
Everyone is accustomed to the MVC frameworks on the server side, but the JS code on the client is often poorly structured.
I suggest to get acquainted with the solution based on backbone.js, underscore.js and jQuery, which will help to solve this problem.
Formulation of the problem
How would we like to see our application? Here are the main points that seem important to me:
- There should be a convenient way to describe the models of our subject area.
- Any changes to the model should immediately be reflected in the user interface if the model is represented in any way.
- Understandable and easy-to-support code structuring in MVC style.
Let's try to solve these problems on the example of a simple application "Movie Catalog".
Instruments
We will need:
- backbone.js
- jQuery
- underscore.js
- mustache.js
- ICanHaz.js
A look at backbone.js
The task of this framework is not to give you a bunch of widgets, and not even to provide a view level. Its task is to give you a few key objects that will help to structure the code.
We need objects Model, Collection, View and Controller.
Model
To get a fully functional model, it’s enough to write just one line of code:
var Movie = Backbone.Model.extend({});
Now we can get object instances, set and get arbitrary attributes:
matrix = new Movie(); matrix.set({ title: "The Matrix", format: "dvd' }); matrix.get('title');
You can also pass attributes directly to the constructor when you create an object:
matrix = new Movie({ title: "The Matrix", format: "dvd' });
You can perform some checks or other actions when creating an object by expanding the model with a function
initialize (). When creating an object, it will be called with the parameter that you passed to the constructor.
var Movie = Backbone.Model.extend({ initialize: function (spec) { if (!spec || !spec.title || !spec.format) { throw "InvalidConstructArgs"; } } });
You can also define the validate () method, it will be called each time you specify attributes and is used to validate attributes. If this method returns anything, the attribute is not set:
var Movie = Backbone.Model.extend({ validate: function (attrs) { if (attrs.title) { if (!_.isString(attrs.title) || attrs.title.length === 0 ) { return "Название должно быть непустой строкой"; } } } });
For a more complete insight into the capabilities of the backbone, I suggest reading the documentation.
Collections
A collection in backbone is an ordered list of models of some type. Unlike a regular array, collections provide much more functionality, such as, for example, setting sorting rules using the comparator () method.
After determining the type of model in the collection, adding an object there is extremely simple:
var MovieList = Backbone.Collection.extend({ model: Movie }); var library = new MovieList(); library.add({ title: "The Big Lebowski", format: "VHS" });
Representation
In general, backbone views define the rules for displaying model changes in the browser.
This is where the DOM manipulations begin and jQuery comes into play. For the initial loading of models into the DOM, we need a template engine, we will use the ICanHaz.js + mustache.js bundle
Here is an example view for our application:
var MovieView = Backbone.View.extend({ render: function() { var context = _.extend(this.model.toJSON(), {cid: this.model.cid}); this.el = ich.movie(context); return this; } });
Let's put it all together
So far we have been talking about different parts of the application, now let's see how to combine them into one whole.
Controller
In the controller, we will associate all parts of the application, as well as define paths for manipulating objects and related methods.
var MovieAppController = Backbone.Controller.extend({ initialize: function(params) { this.model = new MovieAppModel(); this.view = new MovieAppView({ model: this.model }); params.append_at.append(this.view.render().el); }, routes: { "movie/add": "add", "movie/remove/:number": "remove", }, add: function() { app.model.movies.add(new Movie({ title: 'The Martix' + Math.floor(Math.random()*11), format: 'dvd' })); // сбросим путь чтобы метод можно было вызвать еще раз this.saveLocation(); }, remove: function(cid) { app.model.movies.remove(app.model.movies.getByCid(cid)); this.saveLocation(); } });
Here we see that the application model is stored in the controller, which will store all other models and collections, as well as the application view.
The model of the application in our case will store a collection of films:
var MovieAppModel = Backbone.Model.extend({ initialize: function() { this.movies = new MovieList(); } });
The application view will look like this:
var MovieAppView = Backbone.View.extend({ initialize: function() { _.bindAll(this, "addMovie", "removeMovie"); this.model.movies.bind('add', this.addMovie); this.model.movies.bind('remove', this.removeMovie); }, render: function() { $(this.el).html(ich.app(this.model.toJSON())); this.movieList = this.$('#movieList'); return this; }, addMovie: function(movie) { var view = new MovieView({model: movie}); this.movieList.append(view.render().el); }, removeMovie: function(movie) { this.$('#movie_' + movie.cid).remove(); } });
Well, actually the index file with all dependencies and templates:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Movie App</title> <!-- libs --> <script src="js/lib/jquery.js"></script> <script src="js/lib/underscore.js"></script> <script src="js/lib/backbone.js"></script> <!-- templating --> <script src="js/lib/mustache.js"></script> <script src="js/lib/ICanHaz.js"></script> <!-- app --> <script src="js/app/Movie.js"></script> <script src="js/app/MovieCollection.js"></script> <script src="js/app/MovieView.js"></script> <script src="js/app/MovieAppModel.js"></script> <script src="js/app/MovieAppView.js"></script> <script src="js/app/MovieAppController.js"></script> <script type="text/javascript"> $(function() { var movieApp = new MovieAppController({append_at: $('body')}); window.app = movieApp; Backbone.history.start(); }); </script> <!-- ich templates --> <script id="app" type="text/html"> <h1>Movie App</h1> <a href="#movie/add">add new movie</a> <ul id="movieList"></ul> </script> <script id="movie" type="text/html"> <li id="movie_{{ cid }}"><span class="title">{{ title }}</span> <span>{{ format }}</span> <a href="#movie/remove/{{ cid }}">x</a></li> </script> </head> <body> </body> </html>
The whole application is ready. Of course, this is only a very small part of the opportunities that these libraries provide, but I think that this example is enough to get a taste of the development using these tools.
Comments
To leave a comment
Scripting client side JavaScript, jqvery, BackBone
Terms: Scripting client side JavaScript, jqvery, BackBone