В этом руководстве мы создадим приложение, используя CouchDB в качестве нашего бэкэнда и Angular в качестве предпочтительной технологии интерфейса. CouchDB — это база данных NoSQL, а Angular — одна из более новых платформ JavaScript MVC. Удивительно и удивительно то, что CouchDB — это база данных с HTTP API — наше клиентское приложение будет напрямую взаимодействовать с базой данных: CouchDB будет действовать как единственный бэкэнд, который нам нужен для нашего клиентского приложения!
Мы сосредоточимся на небольшом приложении для отслеживания наших расходов. Для каждого шага будет выполняться коммит, а иногда коммит также включает тесты. Тесты не будут темой в этом уроке, но если вы заинтересованы в них, вы должны взглянуть! Весь код, который используется в этом руководстве, вы найдете в репозитории на GitHub .
Почему CouchDB?
Некоторые из вас могут сказать, что мы могли бы использовать альтернативы на стороне клиента. IndexedDB или Local Storage — это технологии, которые локально работают на клиенте для сохранения данных. Но использование сервера базы данных имеет несколько преимуществ: мы можем подключиться к нашему приложению со многими клиентами. Ваш партнер может обновить список расходов, пока вы находитесь в одиночестве в другом супермаркете, также добавив расходы.
Использование CouchDB дает преимущества: CouchDB «говорит» по HTTP изначально, поэтому нам не понадобится еще один слой между нашей базой данных и приложением. Наше приложение JavaScript может напрямую взаимодействовать с базой данных CouchDB с помощью интерфейса RESTful, предоставляемого CouchDB!
И, если бы мы захотели использовать репликацию для нашей базы данных, это было бы так же просто, как нарезать хлеб: CouchDB предназначен для создания распределенных систем баз данных.
Требования
Для этого урока вам потребуется установить последнюю версию CouchDB (1.6) и последнюю стабильную версию Node.js (в настоящее время 0.10.x).
Установка Node.js & Yo
Как пользователь Mac вы можете получить официальный установщик на домашней странице Node . Еще один способ управления установками Node.js в Linux и OSX — это замечательный nvm от Tim Caswell.
Мы установим Йо, чтобы подмостить наше приложение. Йо задаст нам несколько вопросов в процессе создания нашего скелета. Йо спрашивает, хотим ли мы использовать SASS, и если вы не уверены, просто ответьте «нет», но мы определенно хотим включить Bootstrap и предварительно выбранные угловые модули.
В нашей оболочке мы набираем:
npm install -g yo generator-angular grunt-cli couchapp mkdir expenses && cd expenses yo angular expenses
Как часть наших лесов, Йо создал для нас Gruntfile (Gruntfile.js). Grunt — это исполнитель задач в JavaScript с множеством уже написанных плагинов для автоматизации задач и облегчения вашей жизни.
С помощью команды grunt serve сервер разработки запускается, и http://127.0.0.1:9000 должен открываться в браузере после завершения задач grunt. Пример этого показан на следующем рисунке.
Установка CouchDB
Существуют отличные документы по установке CouchDB на многих платформах — есть пакеты для всех основных операционных систем, а в OSX вы можете использовать brew для установки CouchDB.
Первые шаги с CouchDB
Давайте запустим наш первый экземпляр CouchDB и создадим базу данных:
couchdb & # start a CouchDB curl -X PUT http://127.0.0.1:5984/expenses # create the database expenses
CouchDB отвечает:
{"ok":true}
Мы только что создали нашу первую базу данных, используя HTTP!
Давайте подробнее рассмотрим HTTP API CouchDB: теперь мы можем вставить первый документ, скажем, мы хотим отследить какой-то попкорн, который мы купили (нам понадобятся эти вызовы CouchDB позже для нашего приложения).
Теперь мы можем получить доступ к документу, используя запрос GET и идентификатор, который CouchDB присвоил нашему документу, поскольку мы не предоставили конкретный идентификатор:
curl -X GET http://127.0.0.1:5984/expenses/39414de82e814b6e1ca754c61b000efe
Наш клиент будет связываться через HTTP из другого места, чем сам CouchDB. Чтобы сделать это в нашем браузере, нам нужно включить CORS (Cross-Origin Resource Sharing) в CouchDB.
В этом случае мы хотим изменить local.ini для наших локальных пользовательских изменений. Можно изменить конфигурацию через HTTP. В разделе https мы включаем CORS, а затем настраиваем наши источники с подстановочным знаком:
curl -X PUT http://localhost:5984/_config/httpd/enable_cors -d '"true"' curl -X PUT http://localhost:5984/_config/cors/origins -d '"*"'
local.ini двумя командами мы меняем local.ini CouchDB. Вы можете узнать, где находится local.ini используя couchdb -c .
Важный! Обратите внимание, что вы можете изменить исходный раздел, если развернете приложение в производство. Все настройки, представленные здесь, только для разработки!
Угловая и зависимая инъекция
В app/scripts/app.js мы найдем основной файл JavaScript нашего приложения, который на самом деле является так называемым угловым модулем. Этот модуль загружает некоторые другие модули как зависимости (например, ngCookies ). В этом файле мы также находим клиентскую маршрутизацию для нашего приложения с использованием $routeprovider .
$routeprovider в этом файле является хорошим примером внедрения зависимостей Angular (DI). Определяя имя службы, которую вы хотите использовать, Angular вводит ее в заданную область действия функции. Вы можете найти дополнительную информацию о внедрении зависимостей Angular в документах .
Поскольку мы хотим иметь данные, необходимые для подключения к нашей CouchDB, в одном центральном месте, давайте попробуем использовать DI с константой. Мы используем цепочку, чтобы добавить их в наш модуль:
Единственный контроллер, который у нас есть, который был создан во время первоначального скаффолда, это MainCtrl расположенный в app/scripts/controllers/main.jsMainCtrl определен, и $scopeMainCtrl . Мы увидим, как использовать область позже.
Теперь мы можем добавить appSettings к аргументам функции, чтобы внедрить их, как мы видели ранее с $routeprovider :
На следующем шаге мы добавим сервис $http для извлечения данных из нашей CouchDB и обновления представления. В то время как традиционные базы данных работают с данными, которые разлагаются на таблицы, CouchDB использует неструктурированные документы, которые можно объединять, фильтровать и объединять с помощью карты и сокращать функции с помощью концепции, называемой представлениями. Представление определяется проектным документом, особым видом документа.
Вы можете написать представление по своему усмотрению и отправить его в CouchDB с помощью curl, использовать графический интерфейс по адресу http://localhost:5984/_utils или с помощью такого инструмента, как CouchApp — существует множество инструментов, таких как CouchApp ( npm install -g couchapp ), чтобы упростить разработку и развертывание представлений.
_id важен для нас, так как он определяет путь, по которому мы будем запрашивать представление позже. Свойство _design получает префикс _design когда мы создаем проектный документ. Мы byName наше представление по byName и оно просто включает в себя базовую функцию карты, которая будет генерировать свойство name каждого документа в нашей базе данных в качестве ключа и цену в качестве значения.
Если вас интересуют такие инструменты, как CouchApp (подсказка: вы должны будете использовать его позже), вот коммит, который показывает, как его использовать (используйте npm run bootstrap для развертывания проектного документа).
Вы помните наши запросы керлинга в начале? Теперь мы реализуем их в JavaScript. Angular предоставляет сервис $http , который можно внедрить, как показано ниже:
Служба $http возвращает обещание, которое предоставит нам данные JSON из представления CouchDB. Мы добавляем данные в $scope.items . Используя $scope мы можем устанавливать и обновлять значения в нашем представлении. Если значение изменяется в нашей модели, представление автоматически обновляется. Двухстороннее связывание Angular синхронизирует наши данные между представлением и моделью. Он немедленно обновит представление после того, как контроллер изменит модель, а также обновит модель, когда значения в представлении изменятся.
Давайте добавим немного HTML с выражением для отображения наших элементов в app/views/main.html после того, как мы удалили большую часть шаблонной разметки:
Теперь мы должны увидеть первый элемент, но что за остальные элементы?
Здесь мы можем использовать директиву ng-repeat , которая создаст для нас разметку из более длинных списков. В целом можно сказать, что директива в Angular придает поведение элементу DOM. В Angular существует множество других предопределенных директив, и вы также можете определять свои собственные директивы. В этом случае мы добавляем ng-repeat="item in items" во внешний div , который затем будет перебирать items нашего массива из $scope.items .
Классы pull-left и pull-right являются частью Bootstrap CSS и предоставляют нам плавающие элементы. Поскольку элементы плавают, мы применяем clearfix который также включен в Bootstrap:
<divng-repeat="item in items"><divclass="clearfix"><divclass="pull-left">{{ item.key }}</div><divclass="pull-right">{{ item.value }}</div></div></div>
Если вы обновите страницу, элементы будут отображаться в вашем DOM-инспекторе как:
<!-- ngRepeat: item in items --><divng-repeat="item in items"class="ng-scope"><divclass="clearfix"><divclass="pull-left ng-binding">Popcorn</div><divclass="pull-right ng-binding">0.99</div></div></div><!-- end ngRepeat: item in items --><divng-repeat="item in items"class="ng-scope"><divclass="clearfix"><divclass="pull-left ng-binding">Washing powder</div><divclass="pull-right ng-binding">2.99</div></div></div><!-- end ngRepeat: item in items -->
У нас есть небольшой список, но мы не можем отправить новые элементы в наше приложение, кроме использования curl. Приложение до этого момента доступно в этом коммите и показано на следующем рисунке.
Создание формы для отправки товаров
Мы добавим форму с двумя входными данными: один для названия товара, а другой для цены. Форма также получает кнопку для отправки наших товаров.
div с class="row" из Bootstrap используются для адаптивного оформления нашего приложения. Классы Bootstrap, такие как form-control и btn btn-primary , используются для btn btn-primary кнопки и входных данных.
Форма также получает атрибут novalidate : она отключает встроенную проверку формы браузера, поэтому мы можем проверить нашу форму позже, используя Angular:
Используя ng-model мы можем наблюдать и получать доступ к значениям входов в нашем контроллере, а затем отправлять их в CouchDB. Для нашего ввода цены мы добавим атрибут ng-model="price" :
Мы также добавляем небольшое поле статуса под нашим последним элементом. Нам это нужно для отображения ошибок.
<divclass="status"> {{ status }} </div>
Теперь мы можем получить доступ к значениям в нашем контроллере с помощью $scope.price и $scope.name . Объем соединяет вид с нашим контроллером. Глядя на паттерн Model-View-Controller (MVC), область действия будет нашей моделью. Angular иногда также называют MVVM (Model-View-View-Model) Framework — все эти JavaScript MVC-фреймворки часто называют MVW (Model-View-Wh независимо), поскольку между ними существует множество небольших различий.
Но как мы можем отправить форму?
Распространенным способом отправки формы является определение функции в $scope сочетании с директивой ng-submit в представлении. Наша функция создаст JSON, который мы хотим отправить в CouchDB. После создания JSON processForm вызовет postItem который отправит JSON в CouchDB:
Перед отправкой HTTP-запроса в базу данных мы делаем оптимистичное обновление пользовательского интерфейса, поэтому пользователь сразу же видит это обновление, и наше приложение чувствует себя быстрее. Для этого мы добавляем элемент к другим элементам области. Angular обновит представление для нас.
Затем мы выполняем POST-запрос для нашего элемента в фоновом режиме, и в случае успеха мы удаляем любые (предыдущие) сообщения об ошибках из нашего поля состояния.
В случае ошибки мы пишем сообщение об ошибке в представление. CouchDB сообщит нам, почему произошла ошибка в свойстве reason возвращаемого JSON. Чтобы снова получить согласованное представление, мы обновляем список наших товаров после того, как получили ошибку.
В нашу форму мы теперь можем добавить директиву ng-submit которая будет вызывать нашу функцию в области видимости при отправке формы:
Вот и все! Angular очень помогает нам в обновлении нашего обзора! Проверьте последний коммит .
Добавление проверки
Вы могли заметить, что мы можем поместить все виды ценностей в нашу заявку на расходы. Люди могут добавлять в цены недопустимые строки, такие как foo и отправлять их на сервер. Итак, давайте добавим некоторую проверку на стороне сервера: CouchDB может проверять документы при их обновлении. Нам просто нужно добавить поле validate_doc_update с функцией в наш проектный документ. Эта функция должна выдавать исключение в случае неверных данных.
Функция имеет четыре аргумента, как показано ниже:
newDoc — это документ, который будет создан или использован для обновления. Есть также аргументы oldDoc , userCtx и secObj для более сложных secObj , но мы будем просто использовать newDoc для проверки:
Если вы еще не использовали уже упомянутый CouchApp, я действительно рекомендую вам сделать это сейчас, поскольку это значительно облегчает работу с большими проектными документами. Вот проектный документ для CouchApp:
var ddoc ={ _id:'_design/expenses', views:{}, lists:{}, shows:{},validate_doc_update:function(newDoc, oldDoc, userCtx, secObj){if(newDoc._deleted===true){return;}if(!newDoc.name){throw({forbidden:'Document must have an item name.'});}if(!newDoc.price){throw({forbidden:'Document must have a price.'});}if(!/\d+\.\d\d/.test(newDoc.price)){throw({forbidden:'Price must be a number and have two decimal places after a dot.'});}}};// _design/expenses/_view/byName ddoc.views.byName = { map: function (doc) { emit(doc.name, doc.price); } }; module.exports = ddoc;
Имя поля и price не могут быть undefined в нашей проверке. Кроме того, мы проверяем формат цены с помощью регулярного выражения. Если мы просто хотим удалить документ, нам не нужны какие-либо проверки. Мы обновляем наш проектный документ, используя следующую команду:
Удивительно, что у нас сейчас есть какая-то проверка на сервере, но разве не будет еще лучше, если нам не понадобится запрос на проверку нашего документа? Давайте добавим немного проверки с использованием Angular.
Оба наших ввода являются обязательными, поэтому они получают required атрибут. Вы помните наше регулярное выражение в функции проверки нашего конструкторского документа? Директива ng-pattern проверяет наш ввод с помощью регулярного выражения:
Используя name-of-the-form.$invalid мы можем проверить, является ли один из наших входов недействительным. Поскольку наша форма имеет форму атрибута name, мы будем использовать form.$invalid . Мы можем объединить это значение с директивой типа ng-disabled , которая отключит нашу кнопку отправки в случае формы, которая имеет недопустимые или отсутствующие значения:
Это оно! Всего за несколько строк HTML мы получили отличные проверки. Ознакомьтесь с последним коммитом , включая тесты.
Вывод
Мы узнали, как создать небольшое приложение, используя CouchDB и Angular. Angular и CouchDB сделали для нас много тяжелой работы. Мы посмотрели на:
HTTP-интерфейс CouchDB
CouchDB просмотры и проверки
Внедрение зависимостей Angular
Двухстороннее связывание данных Angular
Директивы в угловых
Использование проверки в Angular
Angular и CouchDB являются отличными инструментами для разработки, и они очень помогают нам на пути к работающему приложению. Я надеюсь, что вы получили первое представление о CouchDB и Angular, и если вам интересно, есть еще много тем, на которые вы можете взглянуть: