Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development
Code

Създаване на приложение за управление на Задачи с помощта на Backbone.js

by
Difficulty:BeginnerLength:LongLanguages:

Bulgarian (Български) translation by Mihail Petrov (you can also view the original English article)

Backbone.js е JavaScript framework(работна рамка), с помощта на който можете да създадете гъвкави, Интернет приложения. Backbone.js имплементира в себе си концепции като Модели(Models), Колекции(Collections), Изгледи(Views), Събития (Events) и Рутери (Routes), както и няколко други интересни възможности. В настоящата статия ще разработим малко приложение, реализиращо прост списък със задачи, като проекта ще включва добавяне, редакция и изтриване на задачите в списъка. Ще добавим и възможност за маркиране на определена задача като приключена, както и да я архивираме след това. Всички данни ще бъдат запазвани в рамките на клиентското приложение, като няма да разглеждаме комуникацията му с бази данни или външни услуги.

Подготовка

Файловата структура, която ще ползваме в този урок е показана на изображението:

Някой от директориите са очевидни, като например /css/styles.css за съхранение на стиловете на приложението, както и началната страница на проекта, файла/index.html съдържащ HTML кода. В контекста на Backbone.js Моделите представляват потребителските данни, тоест Задачите, които ще управляваме. Ще организираме Моделите си в Колекция, а бизнес логиката на приложението ни ще разпределим между Изгледите и основният файл на проекта App.js. За целите на проекта ще ползваме Backbone.js заедно с библиотеките Underscore.js и jQuery, от които зависи правилната работа на библиотеката. Всички тези външни библиотеки ще поставим в директорията vendor Сега ни остава само да напишем малко HTML и сме готови да започваме

Добавихме още няколко статични HTML елемента към съдържанието на нашата страница. Добра практика е всички външни библиотеки да бъдат закачени непосредствено преди затварящият </body> таг.

Планиране на приложението:

Винаги е добра идея да планираме разработката преди да сме се захванали с нея. Едно от предимствата на Backbone.j е липсата на стриктна конвенция за конструиране архитектурата на конкретно приложение, което ни дава голяма свобода на действие. Нека преди да започнем имплементирането на бизнес логиката да си поговорим за някой неща свързани с проекта.

Пространство от имена (Namespacing)

Добра практика е при разработката на приложения да отделяме кода в отделни модули. Не е добра идея да оставяме глобални променливи или функции да бъдат достъпни навсякъде в проекта. Това което следва да направим е един Модел, една Колекция, един Рутер и няколко изгледа, като изброени елементи ще бъдат съхранявани във собствени недостъпни за другите модули. Файла App.js ще съдържа механизъм, чрез които ще можем да достъпваме всички описани от нас компоненти..

Както виждате имплементирахме типичен (revealing module pattern). Променливата api е обекта, осигуряващ публичен достъп до всички методи в "класа". Ключовете vies, models и collections, ще съдържат логиката на компонентите ни. Ключа content, е jQuery елемент, указващ основният контейнер, който ще ползваме за потребителски интерфейс в приложението. Налице са също така и два помощни метода. Първият от тях changeContent се грижи за актуализирането на контейнера, а втория title за добавяне на заглавие към страницата ни.

В края на файла създаваме модула ViewsFactory, които ще се грижи за изгледите в приложението ни, както и Рутера на проекта. Може да попитате защо ни е необходима фабрика, за създаване на изгледи? Отговора се крие в няколко често ползвани шаблона при работа с Backbone.js, като един от тях е свързан със създаването и използването на изгледи.

Добра практика е да се създаде инстанция на Изгледа само веднъж, и той да бъде оставен, през целият жизнен цикъл на нашето приложение. Когато част от данните бъдат променени, обикновено извикваме някой от методите на Изгледа и актуализираме съдържанието намиращо се в обекта el Друг много популярен подход е свързан със създаването на нов Изглед, или замяната на цял DOM елемент, но това не е добра практика що се отнася до производителността му. В крайна сметка достигаме до извода, че трябва да създадем помощен клас, който има за задача да създаде една инстанция на нашият Изглед и да я върне при поискване.

Създаване на Компоненти

Нека пристъпим към разработката на компонентите, като започнем с основното меню.

Създаваме си поле, което ще наречем menu, съдържащо в себе си класа за навигацията, като по късно може да добавим методи в нашата фабрика, която ще създаде инстанция от този клас.

Ето я и имплементацията на механизма за управление Изгледите, гарантиращ че винаги ще получаваме само една негова инстанция. Тази техника дава добри резултати в повечето случай.

Стартиране на приложението

Входната точна на нашето приложение, е App.js, изпълняваща метода init(), който ще бъде извикан, при стартиране на приложението, като за целта ще ползваме метода за контролиране на събития при зареждане на страницата onload, на обекта window

След като страницата е заредена, Рутера който дефинирахме по рано поема контрола над приложението. На базата на URL адреса който се зарежда, се взема решение кой компонент да бъде изпълнен. Имайте предвид, че в Backbone.js нямаме типичната Model - View - Controller (Модел - Изглед - Контролер) архитектура. Поради липсата на концепция за Контролер, се налага да дефинираме цялата логика на приложението директно в Изгледите. Необходимо е единствено да свържем методите на Модела с тези на Изгледа и ще получим моментална актуализация на потребителският интерфейс, когато данните в Модела бъдат променени.

Управление на Данните

Най-важното нещо в нашият-малък проект са данните. Решихме че ще правим приложение за управление на задачи, затова е добра идея да започнем с дефинирането на Модела, описващ една единствена Задача.

Модела съдържа само три полета, името на задачата, и два флага дефиниращи състоянието на записа.

Една от най-мощните възможности на Backbone.js представлява механизъм наречен, диспечер за събития(event dispatcher). При всяка променя на данните в Модела, системата изпраща информация за настъпване на събитие, на което може да се отговори подобаващо.

Както споменах в началото, приложението ни ще работи с множество записи, които ще организираме в Колекция.

Метода initialize, e входната точка, за нашата Колекция, в която първоначално ще добавим няколко записа по подразбиране. Останалата част класа, се състой от, методи имплементиращи логика свързана с някой възможности на приложението, което разработваме. Функциите up и down, имат за цел да променят позицията на индивидуалният запис. За да не усложняваме проекта, всеки запис ще се различава от другите само по своята позиция в масива на Колекцията, което означава че ако искаме да вземем индивидуален запис, от Колекцията е необходимо само да знаем неговият индекс. Методите archive и changeStatusр имат за цел да променят флаговете на Моделите, които записваме в Колекцията. Тези методи са част от нея понеже, Изгледите ни ще имат директен достъп само до Колекцията, а не до индивидуалните Модели.

Приключваме, като създаваме инстанция на нашата Колекция.

Първият ни Изглед (Основното Меню)

Първото нещо което трябва да покажем на потребителя, е навигационната лента на нашето приложение.

Кода е доста кратък, но за сметка на това се случват доста интересни неща в него. Първото от което е достъпването на шаблон. В началото на урока, добавихме библиотеката Underscore.js към нашият проект. Въпросната библиотека ни дава наготово мощен механизъм за обработка на шаблони, които ще използваме в приложението си, защото е достатъчно мощен и лесен за употреба.

Функцията приема шаблонен низ и данните, които трябва да подпъхне в него под формата на обект от тип (ключ - стойност). След като разбрахте че функцията приема шаблонен низ сигурно се чудите какво всъщност е $("#tpl-menu").html(), и какво е ролята му в кода. Отговора е прост, поради мащабите на нашият проект можем да си позволим да поставим шаблона, който ще ползваме директно в нашата HTML страница по следният начин:

Понеже кода е опакован със script таг, браузъра не го показва директно на потребителя, но въпреки това той е валиден DOM елемент, които може безпроблемно да бъде достъпен от jQuery

Другият важен метод които заслужава голямо внимание е метода render. Това е функцията, която визуализира данните в Изгледа.

this.$el e обект който Backbone.js, създава във всеки Изглед, по подразбиране ( символа $ пред el, означава че съдържа кеширана версия на jQuery в себе си и може да ползваме методите на библиотеката) По подразбиране обекта съдържа празната двойка тагове <div></div>, но може да съдържа всеки елемент, който пожелаем, като да укажем желанието си като стойност на полето tagName По важното нещо, което трябва да забележим в случая е, че не задаваме стойности на този обект директно, тоест ние не променяме обекта а неговото съдържание. Има голяма разлика между този ред

и този ред

Идеята в случая е, че ако искате да видите промените в браузъра, е необходимо да извикате метода render, преди да сте добавили изгледа към DOM дървото, защото в противен случая няма да бъде добавено съдържанието а само празният <div> елемент.

В крайна сметка изгледа е готов, и остава само да го инициализираме, като за целта първо ще го поставим в създадената по рано фабрика:

А за финал просто ще извикаме метода, визуализиращ менюто.

Забележете че докато, създаваме нова инстанция на навигационният ни клас подавам само аргумент, вече съществуващ елемент от DOM дървото $("#menu"), което означава, че свойството this.$el в Изгледа, всъщност сочи към $("#menu")

Работа с Рутери

Backbone.js поддържа механизъм за манипулиране на URL адресите на браузъра, при придвижване между отделните страници на приложението push state.Въпреки това, ще се придържаме към добрият стар начин за работа с URL използващи #тагове, като този например. /#edit/3.

Току що написахме нашият Рутер, дефиниращ пет различни пътища, с помощта на обект, съдържащ път и функция, която ще бъде изпълнена, когато патят бъде достъпен в URL адреса на браузъра. Забележете че два от адресите имат :index в своите пътища. Това е специален синтаксис, които трябва да използвате ако искате да поддържате динамични URL адреси. В нашият случай ако напишете #edit/3 ще бъде изпълнена функцията editToDo, с параметър index=3. Забележете че последният адрес, съдържа празен низ, отговарящ на началната страница на приложението.

Показване на Списък с всички Задачи

До този момент в нашият проект успяхме да създадем, основният Изглед за нашето приложение. Изгледа ще вземе необходимите ни данни, от нашата Колекцията и ще ги визуализира на екрана. Имаме възможност да използваме един единствен Изглед за визуализация, както на всички активни задачи, така и на вече архивираните.

Преди да продължим, с имплементирането на Изгледа за визуализация на списъка ни със задачи, нека видим как всъщност ще го инициализираме.

Забележете че подаваме Колекцията в рамките на Изгледа. Това е важно понеже по-късно ще ползваме this.model, като средство за достъп на съхранените данни. Фабриката връща Изгледа съдържаш списъка със задачи, но отговорността за добавянето му в рамките на страницата се пада на Рутера.

На този етап метода list, се извиква без параметри, което означава че изгледа ще визуализира само Активните Задачи, а ще пропусне Архивираните такива.

По време на визуализацията на информацията, ще използваме свойството mode. Ако стойността му е mode="archive", ще визуализираме само архивираните задачи. Сега ще насочим вниманието си към празният обект events. Той съдържа събитията, които могат да се изпълнят върху определени DOM елементи, както и кореспондиращите им функции. Например методите priorityUp и priorityDown променят, позицията на задачите в списъка, а метода archive, мести елементите в архивният контейнер на приложението.

Да погледнем метода initialize. По рано казахме че обикновено в него се прави връзка между Моделите, (Колекцията в нашият случай) и метода render, намиращ се изгледа. Можем да използваме следният фрагмент this.model.bind('change', this.render), но много скоро ще осъзнаем че ключовата дума this, намираща се в метода render, няма да сочи към самият Изглед, поради променя на видимостта. Разбира се има начини това недоразумение да бъде заобиколено, като използваме метода bind от библиотеката Underscore.js.

Ето я и имплементацията на метода render.

Обхождаме всички Модели в нашата Колекция и генерираме HTML съдържание, което по-късно ще добавим в нашият Изглед. Правим няколко проверки, които разграничават статута на задачите (архивирани или активни). Задачите се маркират като приключени с помощта на кутийка за отметки (checkbox), като за да идентифицираме, това е необходимо да подадем атрибута checked=="checked" към елемента. Когато закачаме и откачаме Изгледа към DOM дървото, всички събития, които сме закачили към него се премахват, поради тази причина се налага използването на метода this.delegateEvents(), който ще осведоми Backbone.js, че се налага да възстанови събитията. Шаблона който използваме, в рамките на този код е следният:

Забележете наличието на CSS класа done-yes, които има за цел да оцвети, задачите в зелено. Освен това, налице са няколко линкове, които ще ползваме за имплементиране на допълнителна функционалност. Всеки един от тях има атрибут от тип data. Основният лист елемент li, има атрибут data-index, чиято стойност показва позицията на задачата в Колекцията. Забележете израза заграден в символите , това са данните, които се изпращат към шаблонният метод и се заменят с подадената от потребителя нова информация.

Време е да добавим няколко събития към нашият Изглед.

В Backbone.js дефинициите на събития, и техните обработчици, са нищо повече от обект състоящ се от ключ (събитието и елемента върху който се изпълнява то) и стойност (функцията, която се изпълнява при настъпване на събитието).

В този фрагмент, използваме свойството e.target, което сочи към DOM елемента, активирал събитието. Взимаме индекса, на задачата, върху която е кликнал потребителя, като същевременно актуализираме и Модела в рамките на Колекцията. С четирите функции, които току що написахме слагаме край на нашият клас, което ни дава възможност да покажем цялата необходима информация на страницата.

Както вече споменахме, ще използваме същият Изглед, по който работихме за да визуализираме архивираните задачи.

Кода до тук, използва същият Изглед, който ползвахме преди, но този път параметъра, който подаваме е true

Добавяне и Редакция на Задачи

Следвайки примерите до тук, можем да създадем, Изглед който визуализира форма за добавяне и редактиране на задачите в Колекцията. Ето пример как може да създадем новият компонент.

Както виждате на лице е почти същият код, който разглеждаме до момента, но този път трябва да програмираме едно допълнително действие при изпращане на формата, а то е пренасочване на потребителя към началната страница на приложението. Както вече казах, всеки обект, който наследява Backbone.js включва в себе си диспечер на събития, съдържащ методите on и trigger, който ще използваме в проекта.

Преди да продължим с кода, нека да погледнем следният HTML шаблон.

Създаваме си поле за въвъждане името на задачата и бутон за съхранението и.

Този Изглед, е доста кратък, но върши работата си отлично. Закачаме само едно събитие към бутона който трябва да съхрани новата задача. Метода #render, изпълнява различна задача, в зависимост от индекса, който му бива подаден като параметър. Например ако редактираме дадена Задача, просто подаваме нейният индекс и взимаме Модела, който има нужда от редакция, но ако нямаме такъв, тогава просто ще бъде създадена нова задача.В кода до тук има няколко интересни момента. Първият е че в метода render, използваме метода focus(), за да активираме фокуса на формата при зареждане на страницата. Не забравяйте, че трябва да извикаме метода delegateEvents, защото формата може да бъде откачена и закачена отново за страницата. Използваме метода e.preventDefault(), за да премахнем подразбиращото се поведение на бутона за изпращане на формуляра. В края на кода, когато всички операции са извършени, задействаме събитието saved, което да сигнализира, всички услуги, слушащи за това събитие, че задачата е успешно съхранена в Колекцията.

Има още два метода свързани с нашият Рутер които е необходимо да допишем

Разликата между двата метода е в това че метода editToDo, получава индекс, ако е налице път edit/:index, след което се променя заглавието на страницата.

Изтриване на записи от Колекцията

За тази услуга не е необходим отделен Изглед, затова ще реализираме цялата логика в рамките на Рутера

Щом знаем индекса на Задачата, която искаме да изтрием, остава само да намерим необходимият Модел, който ще бъде премахнат от Колекцията. След приключване на операцията пренасочваме потребителя, към началната страница на приложението, което показва актуалната информация до момента.

Заключение

Backbone.js е чудесен инструмент, който съдържа всичко необходимо за създаване на напълно функционално приложение тип (Single page application). Имаме възможност дори да свържем нашият проект със сървърна логика тип REST, като Backbone.js ще синхронизира потока от данни между клиентското приложение и базата данни която ползвате. Подхода за използване на събития (event driven approach), подтиква към създаването на отделни програмни модули, които вървят ръка за ръка с качествената архитектура на вашето приложение. Аз лично ползвам Backbone.js за няколко проекта и съм доста доволен от него.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.