(function($) { var initListeners = function() { /* , , click, keypress, focus ..*/ } var updateListeners = function() { /* , */ } var createUser = function() { /* Ajax */ } var updateUser = function() { /* Ajax */ } var parseResponse = function() { /* JSON- */ } initListeners(); })(jQuery)
This approach may seem like a good option for javascript-code with a length of 100 lines. However, any modification of it can be costly when the code grows into 200 or more lines. The main disadvantages in my opinion are: the presence of routine actions (event management, adding methods to JSON objects), a poorly formalized description of the pageController structure, strong html and javascript connectivity. In fact, often such code rolls into the jumble of nested callbacks and the elusive logic of choosing the level of description of a new function. It was decided to use the framework to give the code structure. The choice fell on the MVVM framework KnockoutJS and here's why: { Id : "", Surname: "", FirstName: "", PatronymicName: "", Login: "", EMail: "" }
It is also known that the server can perform such actions on users as: function DataGate() { var modelStub = { users: [...] }; return { Load : function(callback) { callback(modelStub); }, DeleteUser : function(callback, id) { callback(true); }, SaveOrUpdateUser : function(callback, user) { callback(user); } } } var gate = new DataGate();
Let's define the page display requirements (I will call the page elements as the user represents them, and not how they are implemented in the html code): var viewModel = { selectedUser : ko.observable(null), // deleteSelectedUser : function() { /* users */ }, createUser : function () { /* */ } }
Wait, where are the users ? ViewModel will be expanded by the utility , which will generate observer objects from JSON strings or from POJO objects. This is how the "casting" of simple objects from the server to objects with methods and observer properties ready for use with KNockoutJS is organized. Take a close look at the excerpt from the DataGate code: { users: [...] }
Yes, the viewModel after performing the mapping procedure will have a users field, however this will not be an ordinary array, but an array observer . Moreover, the mapping is performed deeply, which means that the entire object graph from the server will be reduced to the same observer object graph. It is worth noting that the process of such mapping can be flexibly controlled: var mapping = { users: { key: function(data) { return ko.utils.unwrapObservable(data.Id); }, create: function(options) { return new userMapping(options.data); } } }
As you can see, the Id field is used as the key, and the constructor function is used to build the user object: var userMapping = function(user) { ko.mapping.fromJS(user, {}, this); /* user this */ this.FIO = ko.dependentObservable(function() { return this.Surname() + " " + this.FirstName() + " " + this.PatronymicName(); }, this); /* this , user */ var _self = this; this.select = function() { viewModel.selectedUser(_self); } /* */ this.edit = (function() { /* */ })(); /* , */ }
Bind the viewModel to the page: $(function() { gate.Load(function(loadedModel) { ko.mapping.fromJS(loadedModel, mapping, viewModel); ko.applyBindings(viewModel); }); });
<h2> </h2> <div data-bind="jqueryui : 'buttonset'"> <button data-bind="click: createUser"></button> <button data-bind="click: deleteSelectedUser, enable: selectedUser() !== null"></button> </div> <div id="UserListTable"> <div> <table class="list"> <thead> <tr> <th></th> <th></th> </tr> </thead> <tbody data-bind="template: {name: 'editUsersRow', foreach: users, templateOptions: { current: selectedUser } }"> </tbody> </table> </div> </div> <script type="text/html" id="editUsersRow"> <tr data-bind="attr: { 'data-id': Id }, click: select, css: { selected: $data == $item.current() }"> <td><a href="#" data-bind="text: FIO, click: edit"></a></td> <td data-bind="text: Login"></td> </tr> </script>
Some notes on the code: function buildEditUserDlg(model, closeCallback){ return $("#editUserDlg").tmpl().dialog({ title: model.FIO()+" ", /* , . */ width: 400, create: function(e) { var _self = this; ko.applyBindings(model, e.target); /* ViewModel */ model.FIO.subscribe(function(newValue) { /* */ $(_self).dialog("option", "title", newValue+" "); }); model.isOperationComplete.subscribe(function(newValue){ /* ViewModel , , */ if (newValue === true) $(_self).dialog("close"); }) }, close: function(e) { /* , . */ $(this).dialog("destroy").remove(); closeCallback(); }, buttons: { "" : function() { model.save(); }, "": function() { $(this).dialog("close"); } } }); }
It remains to describe the code for opening the dialog for editing (the code for opening for creation is a special case). To do this, go back to the user mapping code: this.edit = (function() { var currentDialog = null; return function() { if (currentDialog != null) { return; } var dialogModel = new userEditDialogMapping(ko.mapping.toJS(_self)); currentDialog = buildEditUserDlg(dialogModel, function() {currentDialog = null}); }; })();
I note that the presence of a dialogue closing kolbek is a necessary measure, which would be nice to get rid of, but I could not make it beautifully. <script type="text/html" id="editUserDlg"> <div> <table> <tbody> <tr> <th><label for="surname">:</label></th> <td><input maxlength="20" name="surname" data-bind="value: Surname" type="text" value=""></td> </tr> <tr> <th class="property"><label for="firstname">:</label></th> <td><input maxlength="20" data-bind="value: FirstName" name="firstname" type="text"></td> </tr> <tr> <th class="property"><label for="patronymicname">:</label></th> <td><input maxlength="20" name="patronymicname" data-bind="value: PatronymicName" type="text" value=""></td> </tr> <tr> <th class="property"><label for="email">E-Mail:</label></th> <td><input maxlength="30" name="email" data-bind="value: EMail" type="text" value=""></td> </tr> <tr> <th class="property"><label for="login">:</label></th> <td><input maxlength="20" name="login" data-bind="value: Login" type="text" value=""></td> </tr> </tbody> </table> </div> </script>
That's all. The client application is ready! PS What is the best way to highlight HTML templates?Source: https://habr.com/ru/post/121926/
All Articles