You may have noticed that more and more web applications have become more complex recently, and the focus is slowly shifting from the server to the client. Is this a normal trend? I have no idea. The debate between those and against is like debating whether the Resurrectionist or Christmas is better; it's hard to say which side is completely correct. Therefore, this article will not discuss which side is right, but I will try to explain that using the well-known object-oriented programming may successfully solve some problems in client programming.
Examples of less standardized code
In order to take into account the responsiveness and user experience of an application, we create increasingly complex code that becomes difficult to understand and maintain. You can easily imagine that the client-side JavaScript application code built without any structure and rules will look like this:
$(function(){ $('#form').submit(function(e) { e.preventDefault(); $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: $('#new-animal').find('textarea').val() }, success: function(data) { $('#animals').append('<li>' + data.text + '</li>'); $('#new-animal').find('textarea').val(''); } }); }); });
Maintaining this type of code will be difficult. Because this short piece of code is related to many places: it controls many events (site, user, network events), it handles user operation events, parses the response returned by the server and generates HTML code. Someone might say: "Yes, you're right, but what if this isn't a client-side single-page application? This is at best an example of overuse of the jQuery library" - not a very convincing point, as everyone knows , easy to maintain and well-designed code is very important. In particular, many tools or frameworks strive to keep code available so that we can more easily test, maintain, reuse, and extend it.
What is MVC?
Speaking of which. We can benefit from those JavaScript frameworks based on MVC, but most of these frameworks do not use MVC, and are equivalent to a combination of Model and Video, or something between the two, which is difficult to distinguish. This is why most Javascript frameworks are based on MV*.
Changing methods may provide organization and architecture for the clients in the project, which makes the code easier to maintain over a long period of time, and even refactoring existing code becomes relatively easy. Knowing how it works and the answers to some of the following questions are essential to remember.
Refactor code using MVC framework
What are the benefits of using MVC to refactor code?
Let’s refactor a typical block of code using some simple steps
Step 1: Create the view and move the Ajax request
We start to remove the dependence on DOM and Ajax. Use the prototype builder, pattern to create the 'Animals' object, and add an 'add' method. At the same time, create the view 'NewAnimalView', and add the methods 'addAnimal', 'appendAnimal', 'clearInput'.
The code is as follows:
var Animals = function() { }; Animals.prototype.add = function (options) { $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: options.text }, success: options.success }); }; var NewAnimalView = function (options) { this.animals = options.animals; var add = $.proxy(this.addAnimal, this); $('# form').submit(add); }; NewAnimalView.prototype.addAnimal = function(e) { e.preventDefault(); var self = this; this.animals.add({ text: $('#new-animal textarea').val(), success: function(data) { self.appendAnimal (data.text); self.clearInput(); } }); }; NewAnimalView.prototype.appendAnimal = function(text) { $('#animals ul').append('<li>' + data.text + '</li>'); }; NewAnimalView.prototype.clearInput = function() { $('#new-animal textarea').val(''); }; $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ animals: animals }); });
Step 2: Use events to remove dependencies.
In this example, using the MVC framework is the key. We will use the event mechanism, which allows us to combine and trigger custom events. Therefore, we create new "AnimalsView" and "NewAnimalView" and give them different responsibilities for displaying animals. It's very simple to separate responsibilities using events. If you pass responsibilities between methods and events like this:
var events = _.clone(Backbone.Events); var Animals = function() { }; Animals.prototype.add = function(text) { $.ajax({ url: '/animals', type: 'POST', dataType: 'json', data: { text: text }, success: function(data) { events.trigger('animal:add', data.text); } }); }; var NewAnimalView = function(options) { this.animals = options.animals; events.on('animal:add', this.clearAnimal, this); var add = $.proxy(this.addAnimal, this); $('# form').submit(add); }; NewAnimalView.prototype.addAnimal = function(e) { e.preventDefault(); this.animals.add($('#new-animal textarea').val()); }; NewAnimalView.prototype.clearInput = function() { $('#new-animal textarea').val(''); }; var AnimalsView = function() { events.on('animal:add', this.appendAnimal, this); }; AnimalsView.prototype.appendAnimal = function(text) { $('#animals ul').append('<li>' + data.text + '</li>'); }; $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ animals: animals }); new AnimalsView(); });
Step 3: Pass the data structure to the core framework
Finally, the most important step, we use: models, views and collections.
var Animal = Backbone.Model.extend({ url: '/animals' }); var Animals = Backbone.Collection.extend({ model: Animal }); var AnimalsView = Backbone.View.extend({ initialize: function() { this.collection.on('add', this.appendAnimal, this); }, appendAnimal: function(animal) { this.$('ul').append('<li>' + animal.escape('text') + '</li>'); } }); var NewAnimalView = Backbone.View.extend({ events: { 'submit form': 'addAnimal' }, initialize: function() { this.collection.on('add', this.clearInput, this); }, addAnimal: function(e) { e.preventDefault(); this.collection.create({ text: this.$('textarea').val() }); }, clearInput: function() { this.$('textarea').val(''); } }); $(document).ready(function() { var animals = new Animals(); new NewAnimalView({ el: $('#new-animal'), collection: animals }); new AnimalsView({ el: $('#animals'), collection: animals }); });
Summary
What have we achieved? We work at a high level of abstraction. Code maintenance, refactoring, and extension become easier. We have greatly optimized the code results. Isn’t it fascinating? marvelous. However, I might want to pour cold water on you, even with the best framework, the code developed is still brittle and difficult to maintain. Therefore, it is a mistake to think that using a better MV* framework can solve all coding problems. Remember that during the refactoring process, the code will become much better after going through the second step, where we do not use the main components of the framework.
Remember that MV* frameworks are good, but all the focus is on the 'How' to develop an application, which leaves it up to the application developer to decide on the 'What'. A complement to each framework, especially when the domain of the project is complex, will be a domain-driven design approach, which will focus more on the following aspects: "what", a process of transforming requirements into real products. But that's another topic we're going to discuss.