*Для тех, кто подумал что я пропустил пробел перед MVC, скажу: нет – JavaScriptMVC – это название фреймворка.
Информации в интернете о нем не так много, поэтому попробуем разобрать вместе как его устанавливать и использовать.
Для начала выкачаем фреймворк себе в директорию проекта либо на уровень выше. Изучаем структуру папок и файлов:
documentjs - документатор funcunit - приложение для тестирования jquery - jquery и его расширения - jQueryMX steal - менеджер подгрузки скриптов и зависимостей js,js.bat - командная строка для Linux/Mac и Windows
Разбор начнем с менеджера подгрузки скриптов steal, с которого все и начинается. Метод steal имеет довольно простой синтаксис:
steal(param1, param2, param3, ...)
В параметре мы можем передавать как путь к файлу, который нужно подгрузить, так и callback-функцию, которая выполнится после загрузки файла. Что касается пути, то можно указывать относительный путь от корня фреймворка и сократить расширение .js(он его добавит сам), т.е.:
steal('jquery/class')
– это значит что он подключит файл %framework_path%/jquery/class.js
Внимание. Так как steal работает асинхронно, то мы должны помещать код, который зависит от подключаемого модуля в callback:
steal('jquery/class', function(){ $.Class })
Идем дальше. Все классы в системе создаются вызовом метода
$.Class([name,] [classProps,] [prototypeProps])
Чтобы создать класс модели используется метод
$.Model(name, classProps, prototypeProps)
Пришло время переходить к реальным примерам. Создаем в корне папку нашего проекта, например “todos“. Помещаем в нее 2 файла: todos.html и todos.js.
todos.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <ul id='todos'></ul> <input id='editor'/> <script type='text/javascript' src='../steal/steal.js?todos/todos.js'></script> </body> </html>
Всего лишь небольшой кусок HTML кода с подключением второго файла – todos.js через steal.js.
Теперь перейдем к более интересному – внутренностям todos.js:
steal('jquery/class', 'jquery/model', 'jquery/dom/fixture', 'jquery/view/ejs', 'jquery/controller', 'jquery/controller/route', function($){ $.Model('Todo',{ findAll : "GET /todos", findOne : "GET /todos/{id}", create : "POST /todos", update : "PUT /todos/{id}", destroy : "DELETE /todos/{id}" },{}); } )
Подключили необходимые модули, создали основную модель. Играем дальше. Чтобы создать объект нашей модели делаем:
var todo = new Todo({name: "do the dishes"});
Появился объект с атрибутом name. Чтобы получить или изменить атрибут используем метод attr.
todo.attr('name') //получит значение атрибута todo.attr('name', "wash the dishes" ); // перезапишет значение
Класс модели использует статические методы для создания, чтения, обновления и удаления сущностей на сервере ( findAll, findOne, create, update, and destroy ), т.е.:
Todo.findAll({})
Но, так как серверной части у нас еще нет, – методы соответственно не отработают так как нам нужно. Хорошо что в фреймворке это учли и сделали заглушки, с помощью которых мы сможем эмулировать серверные ответы. Заглушка реализуется с помощью метода fixture:
$.fixture(url, fixture(original, settings, headers) )
возвратить функция fixture(не путать callback функцию и метод fixture) должна следующую структуру:
return [ status, statusText, responses, responseHeaders ];
например:
return [ 200, "success", {json: []}, {} ];
Добавим в наш код(это который в todos.js) эти заглушки для каждого запроса:
// массив данных var TODOS = [ {id: 1, name: "wake up"}, {id: 2, name: "take out trash"}, {id: 3, name: "do dishes"} ]; $.fixture("GET /todos", function(){ return [TODOS] }); $.fixture("GET /todos/{id}", function(orig){ return TODOS[(+orig.data.id)-1]; }) var id= 4; $.fixture("POST /todos", function(){ return {id: (id++)} }) $.fixture("PUT /todos/{id}", function(){ return {}; }) $.fixture("DELETE /todos/{id}", function(){ return {}; })
Можем проверить как оно работает:
Todo.findAll({}, function( todos ) { console.dir( todos ); })
Для создания и обновления мы вызываем метод save:
// create var todo = new Todo({name: "mow lawn"}) todo.save(function(todo){ console.dir( todo ); }) // update todo.attr("name", "mow my lawn") todo.save(function(todo){ console.dir( todo ); })
Еще мы можем на модель вешать обработчики событий методом bind:
.bind( event, handler(ev, todo ) )
например:
var todo = new Todo({name: "mow lawn"}); todo.bind('created', function(ev, todo){ console.log("created", todo ); }) todo.save()
При вызове todo.save()сработает обработчик события created.
Мы рассмотрели принцы организации моделей во фреймворке, теперь перейдем к представлению. Оно реализовано через метод View:
$.View( idOrUrl, data )
где id0rUrl – ID элемента(либо имя файла), который содержит шаблон, а data – данные передаваемые в этот шаблон. Вот пример шаблона:
<% for(var i = 0; i < this.length; i++ ) { %> <li><%= this[i].name %></li> <% } %>
который мы сохранили в файл todos.ejs. Возвращает этот метод обработанный шаблон:
Todo.findAll( {}, function( todos ){ console.log( $.View( 'todos.ejs', todos ) ); });
в данном случае мы получили элементы для модели и вывели их через шаблон. Также стоит упомянуть что фреймворк расширяет стандартные методы JQuery для работы с HTML, такие как: after, append, before, html, prepend, replaceWith и text. Теперь в них мы тоже можем использовать шаблоны:
$('#todos').html( 'todos.ejs', todos );
Теперь перейдем к организации контроллера, который также основан на вызове метода Controller:
$.Controller(name, classProps, prototypeProps)
с помощью этого метода создается класс JQuery виджета:
$.Controller("Todos", { "init" : function( element , options ){ this.element.html('todos.ejs', Todo.findAll() ) } })
и теперь мы можем создать сам виджет:
new Todos('#todos', {});
который прикрепится к контейнеру #todos и выведет записи по запросу Todo.findAll()
через шаблон todos.ejs.
Тут также не стоит путать виджет контроллера Todos и класс модели Todo.
Обязательно параллельно проверяйте все в коде и экспериментируйте с различными вариантами.
Поговорим более подробно о методе init класса контролера, который будет вызван при создании объекта и имеет следующий синтаксис:
$.Controller.prototype.init(element, options)
где element – это элемент, на который навесили виджет обернутый в JQuery, a options – это опции(как подсказывает кэп), который расширяют настройки заданные по умолчанию. Пример создания нескольких виджетов с параметрами:
//определяем класс $.Controller("Todos", { defaults : {template: 'todos.ejs'} }, { "init" : function( element , options ){ element.html(options.template, Todo.findAll() ) } }) // а теперь создаем виджеты new Todos( document.body.firstElementChild ); new Todos( $('#todos'), {template: 'specialTodos.ejs'})
Также в при создании класса контроллера мы можем сразу же прописать обработчики для событий на элементе виджета следующим образом:
$.Controller("Todos", { "init" : ..., "li click" : function(element, event){ console.log("You clicked", li.text() ) // тригерим событие чтобы дать знать другим element.trigger('selected'); } })
как видно из примера мы имеем следующий синтаксис для навешивания обработчиков:
"[selector] [event]"
Т.е. селектор, причем может быть составной селектор и событие. К сожалению, данный синтаксис не поддерживает множественные события(ну или я не разобрался полностью). Но зато поддерживает динамические шаблоны для подстановки события, выглядит это следующим образом:
"li .destroy {Events.destroy}" : function(el, ev){ ... }
а теперь инициализируем событие:
Events = {destroy: "click"};
Также хорошим стилем является триггер нашего события дальше:
element.trigger('selected');
Т.е. мы отметили что состоялось событие не просто клик, а уже совершилось событие selected.
Удалять созданные виджеты мы можем 2мя способами: стандартно JQuery-style через удаление элемента – $("#todos").remove()
, либо с помощью метода виджета .destroy()
Кроме контроллера виджетов в фреймворке есть еще контроллер роутов, который создается следующим образом:
$.Controller("Routing", { "route" : function(){//сюда попадаем при пустом хеше }, "todos/:id route" : function(data){ //с параметром } }) // создаем контроллер new Routing(document.body);
Как он работает? Он использует метод $.route, который в свою очередь представляет из себя jQuery.Observe наблюдающий за состоянием window.location.hash.
Мы можем непосредственно влиять на hash через этот метод:
$.route.attr('id','6') // location.hash = #!todos/6 // или так var hash = $.route.url({id: 7}) // #!todos/7 location.hash = hash;
Более сложный вариант роутера:
$.Controller("Routing", {init : function(){ this.editor = new Editor("#editor") new Todos("#todos"); }, "route" : function(){ ... }, "todos/:id route" : function(data){ ... }, ".todo selected" : function(el, ev, todo){ $.route.attr('id',todo.id); } }); new Routing(document.body);
Хочу обратить внимание на вариант, когда когда мы выставляем контроллер не на изменение хеша, а на действие .todo selected, но при этом меняем хеш через $.route.attr(‘id’,todo.id).
Из основного это все.