Di Sole

Два примера создания data – таблицы на Dojo Toolkit и JQuery. MVC pattern. Часть 2

Posted in Dojo Toolkit, JavaScript, JQuery by ignar on 01.06.2009

В последнее время замечаю стремительное повышение интереса к Dojo Toolkit,
и решил дописать (незаслуженно отложенное в долгий ящик) продолжение первой статьи.

И так — пошаговое создание приложения таблицы с данными на Dojo Toolkit.

Я постараюсь прокомментировать каждую строчку. Если у Вас возникнут вопросы пишите в комментариях – отвечу с радостью.


Спешу сообщить, что мой адрес поменялся.
Если эта статья Вам интересна — тут она обновлена.

Живой блог лучше чем мертвый.

Новая rss волна — ignar’s log

Мой твиттер остался неизменным — ignart

Мои контакты


Статьи про Dojo Toolkit на DaddyFeed.com

Вступление.

 

Объем кода, по сравнению с jQuery вышел гораздо больше, но это дает и больше возможностей для дальнейшего расширения функционала приложения (можете прокрутить к окончательному скрипту и увидите, насколько разнообразный функционал выйдет).

Скачать рабочий пример можно тут (не забудте sql код с первой части)

Скрипт будет написан с использованием MVC паттерна.
Model — предполагает только доступ к серверу и работу
View — все, что непосредственно связано с html страницы, эфекты, события и сообщения.
Controller — в нем будут основные функции руководящие model и view, а так же, хендлеры событий.

Я не считаю MVC подход манной небесной, зачастую этот паттерн порождает запутанный код и ненужные функции (к счастью в JavaScript существует большое количество подходов), но если использовать палку с умом — можно и не зашибиться.

Начнем обзор с dojo.declare, данный метод реализует принцип наследования, общий функционал:

dojo.declare("/*Название создаваемого класса*/", [/*Наследуемый класс 1*/,/*Наследуемый класс 1*/], {/*Объект с методами создаваемого класса*/})

Если создается класс ни кого не наследующий, вторым параметром передается null.

Есть один зарезервированный метод в dojo.declare — constructor — это конструктор класса.

Небольшой пример вышесказанного:

dojo.declare("App", null, {
    constructor: function(name){
        alert('hello '+name)
    }
});
dojo.addOnLoad(function(){
    var app = new App('user');
});

Конструкторы наследуемых классов вызываются по очереди.

Для запуска скрипта по аналогии с $(document).ready в jQuery в dojo есть метод dojo.addOnLoad, который абсолютно аналогичен.

Поехали дальше. Теперь, когда мы сможем создать классы наших model, view и controller стоит разобраться с системой событий в Dojo Toolkit. Это одна из сильных сторон этого фреймворка.

Есть четыре основных метода позволяющих творить чудеса событий, а именно dojo.connect, dojo.publish, dojo.subscribe, dojo.unsubscribe.

Начнем с dojo.connect и его вариантов использования:

dojo.connect(/*Объект инициирующий событие*/, "/*Название события*/", /*Объект, в области которого мы должны действовать*/, "/*Метод исполняемого объекта*/");
dojo.connect(/*Объект инициирующий событие*/, "/*Название события*/", /*Функция, которую следует вызвать при событии*/);

Несколько примеров:

dojo.connect(dojo.query('#button'), 'onclick', function(){ alert('hello'); })
dojo.connect(myObj, 'someMyObjMethod', AnotherObj, 'AnotherObjMethod');

При выполнении someMyObjMethod будет вызван AnotherObjMethod.

Далее система «dojo.publish — dojo.subscribe» — реализует систему публикация — подписчик

dojo.publish — позволяет опубликовать нужное событие, использование:

dojo.publish("/*Метка или имя события*/", [/*Данные которые можно передать при событии*/])

Использование:

dojo.publish('/say/hello',[{word: 'hello'}]);

Взаимодополняющая функция dojo.subscribe — определяет, как реагировать на событие:

dojo.subscribe(‘/*Метка или имя события*/’,/*Функция, реагирующая на событие*/)

Пример:

dojo.subscribe('/say/hello', function(message){ alert('I received word'+message.word); })

Хочу обратить внимание, что подписка на события через звездочку более не работает, с какой-то там версии, и вот такая подписка не сработает

dojo.subscribe('/say/*', function(message){ alert('I received word'+message.word); })

Не работает так же и

dojo.require("dijit.form.*");

Хочу обратить на это внимание, так как в поисках информации очень часто наталкиваюсь вот на такие примеры, которые являются устарелыми и не работают.

dojo.subscribe — возвращает хендлер данной подписки и что бы больше не реагировать на события, существует dojo.unsubscribe

var handle = dojo.subscribe('/say/*', function(message){ alert('I received word'+message.word); })
dojo.unsubscribe(handle);

Пишем приложение.

 

В нашем приложении будут использоваться компоненты dijit.Dialog — окошко, кнопки — dijit.form.Button и вспомогательный компонент behavior (dojo.behavior), который значительно облегчит нам жизнь.

Для использования всех этих благ надо подключить скрипты и зависимости для них, скрипт на данном этапе будет таким:

dojo.require("dojo.behavior");
dojo.require("dijit.form.Button");
dojo.require("dijit.Dialog");

dojo.addOnLoad(function(){
    /*тут будет основной код*/
});

Это первые кирпичики нашего приложения. Давайте назовем, по аналогии с первой частью, файл init.dojo.js и сохраним в /lib/

Что бы у нас что то заработало, надо подключить ядро dojo, для этого в хедере html страницы добавляем:

<link type="text/css" rel="stylesheet" href="/lib/dojo/dijit/themes/soria/soria.css" /> /*подключаем CSS стиль для dijit виджетов*/
<script type="text/javascript" src="/lib/dojo/dojo/dojo.js"></script> /*подключаем ядро dojo*/
<script type="text/javascript" djConfig="parseOnLoad: true, isDebug: true"></script> /*опции dojo*/
<script type="text/javascript" src="/lib/init.dojo.js"></script>

parseOnLoad — указывает, что нужно пропарсить страницу автоматически для инициализации виджетов
isDebug — включаем дебаг в нашем приложении, внизу появится FireBug light (если он у вас уже не установлен)

Теперь, когда вы все это сделаете в FireBug, сможете увидеть, как dojo подключает все необходимые зависимости.

firebug

Самым простым для начала будет инициализация внешнего вида таблицы. За это отвечает, по нашей логике, view компонент,
допишем после строчек dojo.require следующее

dojo.declare('AppView', null, {
	constructor: function(){
		dojo.addClass(dojo.body(),'soria');
		this._initGridView();
	},
	_initGridView: function(){
		dojo.behavior.add({
			'.grid tbody tr:nth-child(even)': {
				found: function(e){
					dojo.addClass(e, 'even');
				}
			},
			'.grid tbody tr': {
				onmouseenter: function(e){
					dojo.addClass(e.currentTarget, 'hover');
				},
				onmouseleave: function(e){
					dojo.removeClass(e.currentTarget, 'hover');
				},
				onclick: function(e){
					dojo.toggleClass(e.currentTarget, 'clicked');
					dojo.publish('/row/clicked',[{id:e.currentTarget.id}]);
				}
			},
			'.grid .edit': {
				onclick: function(e){
					dojo.publish('/controll/prepareEdit',[{id:e.currentTarget.id}]);
				}
			},
			'.grid .add': {
				onclick: function(e){
					dojo.publish('/controll/prepareAdd',[]);
				}
			},
			'.grid .delete': {
				onclick: function(e){
					dojo.publish('/controll/prepareDelete',[]);
				}
			}
		});
		dojo.behavior.apply();
	}
});

dojo.addOnLoad(function(){
    var view = new AppView();
});

При создании сущности класса AppView первым делом вызывается конструктор, который исполняет _initGridView

dojo.addClass(dojo.body(),’soria’); — добавит к тегу body нужный нам класс soria. Тут подробнее, зачем это делать.

Синтаксис dojo.behavior прост, находим так же как в dojo.query нужный элемент или группу и что делать при определенном событии связанным с этим элементом, данный расширение ядра позволяет ненавязчиво определить все события на странице и применить их, если нужно, повторно.

dojo.behavior.apply() — инициализирует события.

Сейчас наш грид стал реагировать на события мыши.

Создадим диалоги для редактирования и подтверждения удаления, для этого допишем в класс AppView следующий метод и добавим в конструктор:

	_initDialogs: function(){
		this.formDialog = new dijit.Dialog({title: dojo.attr('dialog', 'title')},dojo.byId('dialog'));
		this.formDialog.startup();
		this.confirmDialog = new dijit.Dialog({title: dojo.attr('confirm', 'title')},dojo.byId('confirm'));
		this.confirmDialog.startup();
	}

При программном создании виджетов в dijit первым параметром передается объект опций, вторым элемент к которому прикрепить виджет.

Присваиваем объекты диалогов созданных виджетов к AppView для удобства.

dojo.attr(‘dialog’, ‘title’) — находит элемент с id — dialog и возвращает значение атрибута title.

Можно было сделать по другому:

dojo.attr(dojo.query(‘#dialog’), ‘title’)

Эфект один и тот же.

this.formDialog.startup() — инициализирует диалоги.

Вот такая должна быть структура нашего класса.

dojo.declare('AppView', null, {
	constructor: function(){
		dojo.addClass(dojo.body(),'soria');
		this._initGridView();
        this._initDialogs();
	},
	_initGridView: function(){/* */},
    _initDialogs: function(){/* */}
});

Теперь, когда у нас есть диалоговые окна нужно к ним добавить кнопки, у нас их всего четыре — потому инициализируем каждую из них:

	_initButtons: function(){
        // добавляем html в нужное место
		dojo.query('#dialogForm div:last-child').addContent('<div><button id="update">Сохранить</button>&nbsp;<button id="cUpdate">Отменить</button></div>','after');
        // создаем кнопку подтверждения сохранения
		this.buttonUpdate = new dijit.form.Button({},'update');
        // создаем кнопку отмены
		this.buttonCUpdate = new dijit.form.Button({},'cUpdate');
        // добавляем публикацию события при клике по каждую из кнопок
		this.buttonUpdate.onClick = function(e){
			dojo.publish('/controll/update',[]);
		};

		this.buttonCUpdate.onClick = function(e){
			dojo.publish('/controll/cancel',[]);
		};
        // по аналогии кнопки окна подтверждения удаления
		dojo.query('.dialogBody').addContent('<div><button id="delete">Удалить</button>&nbsp;<button id="cDelete">Отменить</button></div>');
		this.buttonDelete = new dijit.form.Button({},'delete');
		this.buttonCDelete = new dijit.form.Button({},'cDelete');

		this.buttonDelete.onClick = function(e){
			dojo.publish('/controll/delete',[]);
		};
        // отмена будет действовать одинаково во всех случаях - закрывать все окна
		this.buttonCDelete.onClick = function(e){
			dojo.publish('/controll/cancel',[]);
		};
	}

Добавляем вызов _initButtons в конструкторе.

Нам нужны объекты model и controller’a:

dojo.declare('AppModel',null, {
	constructor: function(){
        // сразу создадим переменную которая
        // будет хранить массив выделенных рядов грида
		this.selectedRows = [];
	}
});

// конструктор контроллера будет принимать сущности view и модели
dojo.declare('AppController', null, {
	constructor: function(model, view){
		this.model = model;
		this.view = view;
        // метод который определит подписки на 
        // события и функции реагирующие на них
		this._initSubscribers();
	},
    // сейчас у нас их не много
	_initSubscribers: function(){
		dojo.subscribe('/row/clicked',this,'rowClicked');
		dojo.subscribe('/controll/prepareEdit',this,'controllPrepareEdit');
		dojo.subscribe('/controll/prepareAdd',this,'controllPrepareAdd');
		dojo.subscribe('/controll/prepareDelete',this,'controllPrepareDelete');
		dojo.subscribe('/controll/update',this,'controllUpdate');
		dojo.subscribe('/controll/delete',this,'controllDelete');
		dojo.subscribe('/controll/cancel',this,'controllCancel');
	}
});

Дописываем следующие недостающие для обработки событий методы контроллеру:

dojo.declare('AppController', null, {
	constructor: function(model, view){
		this.model = model;
		this.view = view;
		this._initSubscribers();
	},

    _initSubscribers: function(){/* */},

    // медот реагирует на клик по ряду таблици
    // вызывая метод toggleSelectedRow модели
	rowClicked: function(mess){
		this.model.toggleSelectedRow(mess.id);
	},

    // метод открывает диалог редактирования
	controllPrepareEdit: function(mess){
        // получаем данные строки в html таблицы
        // с помощью метода getRowData модели
		var data = this.model.getRowData(mess.id);
        // заполняем форму диалога редактирования
		this.view.setFormDialog(data);
        // открываем дилалог
		this.view.showFormDialog();
        // посколько диалог редактирования и добавления
        // у нас один, нам необходимо знать,
        // что было последним
		this.lastAction = 'edit';
	},

    // метод открывает диалог добавления новой записи
	controllPrepareAdd: function(mess){
        // очищаем форму если она была перед этим заполнена
        // по аналогии с примеров на jQuery
		this.view.clearFormDialog();
        // открываем дилалог
		this.view.showFormDialog();
        // посколько диалог редактирования и добавления
        // у нас один, нам необходимо знать,
        // что было последним
		this.lastAction = 'add';
	},

    // вызываем диалог подтверждения удаления
	controllPrepareDelete: function(mess){
		this.view.showConfirmDialog();
	},

    // вызывается при нажатии на кнопку сохранения
	controllUpdate: function(mess){
        // получаем данные форму диалога
		var data = this.model.getFormData();
        // узнаем, что надо делать
		if (this.lastAction === 'edit') {
            // вызываем соответствующий метод модели
			this.model.xhrUpdate(data);
		} else if (this.lastAction === 'add') {
			this.model.xhrAdd(data);
		}
        // прячем окно
		this.view.formDialog.hide();
	},
    // далее по аналогии - соответствующий метод модели
    // и прячем с помощью view
	controllDelete: function(mess){
		this.model.xhrDelete();
		this.view.confirmDialog.hide();
	},

	controllCancel: function(mess){
		this.view.formDialog.hide();
		this.view.confirmDialog.hide();
	}
});

Вот так вот должен выглядеть класс модели нашего приложения

dojo.declare('AppModel',null, {
	constructor: function(){
		this.selectedRows = [];
	},
    // метод удаления или добавления id выделенного ряда
	toggleSelectedRow: function(id){
		// смотрим, есть ли такой элемент в массиве
		var index = this.selectedRows.indexOf(id);
		if (index == -1) {
			// если нет - добавляем
			this.selectedRows.push(id);
		} else {
			// если есть удаляем
			this.selectedRows.splice(index, 1);
		}
	},
    // очищение выделенных строк
    removeSelectedRows: function(){
        this.selectedRows = [];
    },
    // метод возвращает объект с парами ключ - значения
    // ряда таблицы по его id
	getRowData: function(rowId){
		var data = {};
		dojo.query('.grid tbody #'+rowId+' div').forEach(function(v, k){
			data[dojo.attr(v,'class')] = v.innerHTML;
		});
		return data;
	},
    // обновляем данные ряда по его id
	setRowData: function(data){
		dojo.query('.grid tbody #'+data.id_user+' div').forEach(function(v, k){
			v.innerHTML = data[dojo.attr(v,'class')];
		});
	},
    // вставляет новый ряд
	insertRowData: function(data){
		var tbody = dojo.query('.grid tbody')[0];
        // полностью обновляем содержимое tbody
        tbody.innerHTML += data;
        // по этому заново иницаилизируем события на странице
        dojo.behavior.apply();
	},
    // удаляем строки с грида по id хранящимся в переменной selectedRows
	removeRow: function(){
		dojo.forEach(this.selectedRows,function(k,v){
			dojo.query('.grid #'+k).orphan();
		});
	},
    // отдает данные формы диалога редактирования
    // в парах ключь - значение
	getFormData: function(){
		var data = {};
		dojo.query('#dialogForm input').forEach(function(v, k){
			data[v.id] = v.value;
		});
		return data;
	},
    // создает xhr запрос на сохранение
	xhrUpdate: function(data){
		dojo.xhrPost({
            // куда обращаемся
			url: '/save.php',
            // какого типа ждать ответ от сервера
			handleAs: 'text',
            // то, что передаем
			content: data,
			load: function(d){
                // при успешном выполнении оповещаем об этом
				dojo.publish('/xhr/update',[{result: 'success', action: 'update', data: data}]);
			},
			error: function(e){
                // или публикуем ошибку
				dojo.publish('/xhr/update',[{result: 'error', action: 'update'}]);
			}
		});
	},

	xhrAdd: function(data){
		dojo.xhrPost({
			url: '/insert.php',
			handleAs: 'text',
			content: data,
			load: function(d){
				dojo.publish('/xhr/add',[{result: 'success', action: 'add', data: d}]);
			},
			error: function(d){
				dojo.publish('/xhr/add',[{result: 'error', action: 'add'}]);
			}
		});
	},

	xhrDelete: function(){
        var rows = this.selectedRows;
		dojo.xhrGet({
			url: '/delete.php',
			handleAs: 'text',
			content: {'ids[]': this.selectedRows},
			load: function(d){
				dojo.publish('/xhr/delete',[{result: 'success', action: 'delete', data: rows}]);
			},
			error: function(d){
				dojo.publish('/xhr/delete',[{result: 'error', action: 'delete'}]);
			}
		});
	}
});

Добавляем в конструктор контроллера подписки на результат xhr запрос

		dojo.subscribe('/xhr/delete', this, 'controllXhrDelete');
		dojo.subscribe('/xhr/update', this, 'controllXhrUpdate');
		dojo.subscribe('/xhr/add', this, 'controllXhrAdd');

и добавляем к нему функции:

	controllXhrDelete: function(mess){
        // при удачном выполнении запрос на удаление
        if (mess.result === 'success'){
            // удаляем ряды с таблицы
			this.model.removeRow();
            // очищаем хранимые выделенные ряды
            this.model.removeSelectedRows();
            // сообщаем, что все прошло успешно
            this.view.showDoneXhr()
		} else {
            // показываем сообщение об ошибке
			this.view.showError();
		}
	},

    controllXhrUpdate: function(mess){
        if (mess.result === 'success'){
            // обновляем редактируемый ряд
			this.model.setRowData(mess.data);
            this.view.showDoneXhr()
		} else {
			this.view.showError();
		}
    },

    controllXhrAdd: function(mess){
		if (mess.result === 'success'){
            // вставляем новый ряд в таблицу
			this.model.insertRowData(mess.data);
            this.view.showDoneXhr()
		} else {
			this.view.showError();
		}
    }

Окончательно все будет выглядеть вот так:

dojo.require("dojo.behavior");
dojo.require("dijit.form.Button");
dojo.require("dijit.Dialog");

dojo.declare('AppView', null, {
	constructor: function(){
		dojo.addClass(dojo.body(),'soria');
		this._initButtons();
		this._initDialogs();
		this._initGridView();
        this._xhrEvents();
	},

    _xhrEvents: function() {
        dojo.connect(dojo,'xhrPost', this, 'showStartXhr');
        dojo.connect(dojo,'xhrGet', this, 'showStartXhr');
    },

	_initButtons: function(){
		dojo.query('#dialogForm div:last-child').addContent('<div><button id="update">Сохранить</button>&nbsp;<button id="cUpdate">Отменить</button></div>','after');
		this.buttonUpdate = new dijit.form.Button({},'update');
		this.buttonCUpdate = new dijit.form.Button({},'cUpdate');

		this.buttonUpdate.onClick = function(e){
			dojo.publish('/controll/update',[]);
		};

		this.buttonCUpdate.onClick = function(e){
			dojo.publish('/controll/cancel',[]);
		};

		dojo.query('.dialogBody').addContent('<div><button id="delete">Удалить</button>&nbsp;<button id="cDelete">Отменить</button></div>');
		this.buttonDelete = new dijit.form.Button({},'delete');
		this.buttonCDelete = new dijit.form.Button({},'cDelete');

		this.buttonDelete.onClick = function(e){
			dojo.publish('/controll/delete',[]);
		};

		this.buttonCDelete.onClick = function(e){
			dojo.publish('/controll/cancel',[]);
		};
	},

	_initDialogs: function(){
		this.formDialog = new dijit.Dialog({title: dojo.attr('dialog', 'title')},dojo.byId('dialog'));
		this.formDialog.startup();
		this.confirmDialog = new dijit.Dialog({title: dojo.attr('confirm', 'title')},dojo.byId('confirm'));
		this.confirmDialog.startup();
	},

	_initGridView: function(){
		dojo.behavior.add({
			'.grid tbody tr:nth-child(even)': {
				found: function(e){
					dojo.addClass(e, 'even');
				}
			},
			'.grid tbody tr': {
				onmouseenter: function(e){
					dojo.addClass(e.currentTarget, 'hover');
				},
				onmouseleave: function(e){
					dojo.removeClass(e.currentTarget, 'hover');
				},
				onclick: function(e){
					dojo.toggleClass(e.currentTarget, 'clicked');
					dojo.publish('/row/clicked',[{id:e.currentTarget.id}]);
				}
			},
			'.grid .edit': {
				onclick: function(e){
					dojo.publish('/controll/prepareEdit',[{id:e.currentTarget.id}]);
				}
			},
			'.grid .add': {
				onclick: function(e){
					dojo.publish('/controll/prepareAdd',[]);
				}
			},
			'.grid .delete': {
				onclick: function(e){
					dojo.publish('/controll/prepareDelete',[]);
				}
			}
		});
		dojo.behavior.apply();
	},

	setFormDialog: function(data){
		dojo.query('#dialogForm input').forEach(function(v,k){
			v.value = data[v.id];
		});
	},

	clearFormDialog: function(){
		dojo.query('#dialogForm input').forEach(function(v,k){
			v.value = '';
		});
	},

	showFormDialog: function(){
		dojo.query('#dialog').style('display','block');
		this.formDialog.show();
	},

	showConfirmDialog: function(){
		dojo.query('#confirm').style('display','block');
		this.confirmDialog.show();
	},

	showError: function(mess){
		dojo.query('#indicator').addContent("Возникла ошибка при выполнении");
	},

    showStartXhr: function(){
        dojo.query('#indicator').empty().addContent("В процессе");
        dojo.query('#indicator').addContent(
            dojo.clone(dojo.byId('loader'))
        );
        dojo.query('#loader').style('display','inline');
    },

    showDoneXhr: function(){
        dojo.query('#indicator').empty().addContent("Выполнено");
    }
});

dojo.declare('AppModel',null, {
	constructor: function(){
		this.selectedRows = [];
	},

	toggleSelectedRow: function(id){
		// смотрим есть ли такой элемент в массиве
		var index = this.selectedRows.indexOf(id);
		if (index == -1) {
			// если нет - добавляем
			this.selectedRows.push(id);
		} else {
			// если есть удаляем
			this.selectedRows.splice(index, 1);
		}
	},

    removeSelectedRows: function(){
        this.selectedRows = [];
    },

	getRowData: function(rowId){
		var data = {};
		dojo.query('.grid tbody #'+rowId+' div').forEach(function(v, k){
			data[dojo.attr(v,'class')] = v.innerHTML;
		});
		return data;
	},

	setRowData: function(data){
		dojo.query('.grid tbody #'+data.id_user+' div').forEach(function(v, k){
			v.innerHTML = data[dojo.attr(v,'class')];
		});
	},

	insertRowData: function(data){
		var tbody = dojo.query('.grid tbody')[0];
        tbody.innerHTML += data;
        dojo.behavior.apply();
	},

	removeRow: function(){
		dojo.forEach(this.selectedRows,function(k,v){
			dojo.query('.grid #'+k).orphan();
		});
	},

	getFormData: function(){
		var data = {};
		dojo.query('#dialogForm input').forEach(function(v, k){
			data[v.id] = v.value;
		});
		return data;
	},

	xhrUpdate: function(data){
		dojo.xhrPost({
			url: '/save.php',
			handleAs: 'text',
			content: data,
			load: function(d){
				dojo.publish('/xhr/update',[{result: 'success', action: 'update', data: data}]);
			},
			error: function(e){
				dojo.publish('/xhr/update',[{result: 'error', action: 'update'}]);
			}
		});
	},

	xhrAdd: function(data){
		dojo.xhrPost({
			url: '/insert.php',
			handleAs: 'text',
			content: data,
			load: function(d){
				dojo.publish('/xhr/add',[{result: 'success', action: 'add', data: d}]);
			},
			error: function(d){
				dojo.publish('/xhr/add',[{result: 'error', action: 'add'}]);
			}
		});
	},

	xhrDelete: function(){
        var rows = this.selectedRows;
		dojo.xhrGet({
			url: '/delete.php',
			handleAs: 'text',
			content: {'ids[]': this.selectedRows},
			load: function(d){
				dojo.publish('/xhr/delete',[{result: 'success', action: 'delete', data: rows}]);
			},
			error: function(d){
				dojo.publish('/xhr/delete',[{result: 'error', action: 'delete'}]);
			}
		});
	}
});

dojo.declare('AppController', null, {
	constructor: function(model, view){
		this.model = model;
		this.view = view;
		this._initSubscribers();
	},

	_initSubscribers: function(){
		dojo.subscribe('/row/clicked',this,'rowClicked');
		dojo.subscribe('/controll/prepareEdit',this,'controllPrepareEdit');
		dojo.subscribe('/controll/prepareAdd',this,'controllPrepareAdd');
		dojo.subscribe('/controll/prepareDelete',this,'controllPrepareDelete');
		dojo.subscribe('/controll/update',this,'controllUpdate');
		dojo.subscribe('/controll/delete',this,'controllDelete');
		dojo.subscribe('/controll/cancel',this,'controllCancel');
		dojo.subscribe('/xhr/delete', this, 'controllXhrDelete');
		dojo.subscribe('/xhr/update', this, 'controllXhrUpdate');
		dojo.subscribe('/xhr/add', this, 'controllXhrAdd');
	},

	rowClicked: function(mess){
		this.model.toggleSelectedRow(mess.id);
	},

	controllPrepareEdit: function(mess){
		var data = this.model.getRowData(mess.id);
		this.view.setFormDialog(data);
		this.view.showFormDialog();
		this.lastAction = 'edit';
	},

	controllPrepareAdd: function(mess){
		this.view.clearFormDialog();
		this.view.showFormDialog();
		this.lastAction = 'add';
	},

	controllPrepareDelete: function(mess){
		this.view.showConfirmDialog();
	},

	controllUpdate: function(mess){
		var data = this.model.getFormData();
		if (this.lastAction === 'edit') {
			this.model.xhrUpdate(data);
		} else if (this.lastAction === 'add') {
			this.model.xhrAdd(data);
		}
		this.view.formDialog.hide();
	},

	controllDelete: function(mess){
		this.model.xhrDelete();
		this.view.confirmDialog.hide();
	},

	controllCancel: function(mess){
		this.view.formDialog.hide();
		this.view.confirmDialog.hide();
	},

	controllXhrDelete: function(mess){
        if (mess.result === 'success'){
			this.model.removeRow();
            this.model.removeSelectedRows();
            this.view.showDoneXhr()
		} else {
			this.view.showError();
		}
	},

    controllXhrUpdate: function(mess){
        if (mess.result === 'success'){
			this.model.setRowData(mess.data);
            this.view.showDoneXhr()
		} else {
			this.view.showError();
		}
    },

    controllXhrAdd: function(mess){
		if (mess.result === 'success'){
			this.model.insertRowData(mess.data);
            this.view.showDoneXhr()
		} else {
			this.view.showError();
		}
    }
});

dojo.addOnLoad(function(){
	var model = new AppModel();
	var view = new AppView();
	var controller = new AppController(model, view);
});

Вот то, что вышло в итоге:

tree

Скачать рабочий пример можно тут (не забудте sql код с первой части)

И как оно выглядит:

грид jquery dojo toolkit

грид jquery dojo toolkit

Заключение

 

У меня реализация задуманного на jQuery заняло 243 строчки, а на Dojo Toolkit 324.

Стоит учесть, что при использовании MVC выходит несколько больше кода из-за разнесенного функционала.

На Dojo Toolkit код вышел функциональней и гораздо лучше расширяемый.

В статистике блога часто замечаю поисковые слова вроде «jquery vs dojo» и им подобные.

Очень хотелось бы, расставить точки над «i» 🙂 и направить сомневающихся.

Если ваша задумка вполне реализуема на jQuery, то не стоит использовать, что-то тяжелее. jQuery занимает меньше кода и легче в понимании, сообщество у него просто мега-огромное.

Базовый скрипт Dojo Tookit покрывает возможности jQuery, но дальше – больше.

Если вы захотите создать окно, Dojo потянет за собой локализацию и еще кучу вещей которые вам скорей всего не нужны. НО – если они вам нужны в jQuery этого нет.

Управлять и настраивать виджеты в Dojo Toolkit легко. В jQuery это зачастую просто не предусмотрено.

В dijit вообще много того, что в jQuery UI не реализовано.

А если начинать с чего то, то лучше с jQuery, а потом двигаться к чему-то более большому.

Реклама

Один ответ

Subscribe to comments with RSS.

  1. […] Два примера создания data – таблицы на Dojo Toolkit и JQuery. Часть 1. Часть 2 […]


Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: