Angular boilerplate. Простота — тренд молодежи

от автора

Любая физическая система стремится к состоянию с наименьшей потенциальной энергией. И программисты не исключение. Поэтому речь пойдет о том, как упростить себе жизнь при разработке на angular.js, используя при этом сервисы, которые сейчас в тренде. Главным образом, я буду ненавязчиво пиарить свое архитектурное решение angular-boilerplate, а на закуску предложу поделиться своим опытом и идеями в комментариях.

Мотивация

Свести рутину к минимуму, создать интуитивно понятную архитектуру и собрать вместе то, что называется best practices.

Сразу оговорюсь, что angular-boilerplate — результат создания с нуля нескольких проектов разной степени сложности, в процессе работы над которыми я всячески старался избавиться от ненужного усложнения, в первую очередь, собственного рабочего процесса. Поэтому в чем-то мои решения вызваны субъективным взглядом на разработку, но тем не менее все они прошли проверку временем и показали практическую пользу.

Особенности

Grunt

Начнем с того, что нам понадобится grunt.js. Grunt нужен будет, в первую очередь, для того, чтобы собирать и сжимать файлы кода и стилей. В проекте уже есть две задачи в Gruntfile.js: grunt install и grunt build — первая позволяет выполнять различные операции при установке и в начальном варианте производит установку необходимых библиотек через bower, вторая, как я уже писал выше собирает и минифицирует файлы с помощью require.js.

Bower

Чтобы лишний раз не бегать на гитхаб, библиотеки проще подгружать через bower. Здесь я отмечу файл .bowerrc, в котором просто можно определить путь и имя папки модулей.

Require.js

Практика, когда библиотеки и код подлючаются в html странице, лично у меня вызывает когнитивный диссонанс. И я всегда хотел иметь в javascipt привычную для других языков возможность програмно подключать код и определять зависимости. Поэтому я с большим энтузиазмом отношусь к Require.js и применению его в качестве каркаса для приложения. В этом месте я, пожалуй, опущу детальное введение в Require.js, а просто укажу на то, как он используется в проекте.

Итак, главное, что нам дает require.js, помимо модульности — возможность свести всё приложение в один файл и затем сжать. Но, разумеется, при разработке хотелось бы иметь дело с нормальной версией файлов, поэтому в индексе есть два варианта подключения логики и стилей:

<!--Development--> <link rel="stylesheet" href="app/styles/styles.css"> <!--Production--> <link rel="stylesheet" href="app/styles/styles.min.css">  <!--Development--> <script data-main="app/js/app" src="app/lib/requirejs/require.js"></script> <!--Production--> <script src="app/js/app.min.js"></script> 

Которые позволяют переключаться между development и production.

Все библиотеки, модули и прочее при этом подключаются при инициализации приложения, и их настройки определяются в файле app.js:

require.config({     baseUrl: 'app',     paths: {         'jquery': 'lib/jquery/dist/jquery',         'angular': 'lib/angular/angular',         'text': 'lib/requirejs-text/text'     },     shim: {         'jquery': {             exports: 'jQuery'         },         'angular': {             exports: 'angular',             'deps': ['jquery']         },         'lib/angular-route/angular-route': {             'deps': ['angular']         }     },     config: {         'js/services': {             apiUrl: 'http://localhost/api'         }     } }); 

Далее пройдемся по файлам.

Файлы

/app      /js           app.js            app.min.js            controllers.js            directives.js            filters.js            services.js       /styles           styles.js            styles.min.js       /templates           someone-template.html index.html bower.json package.json .bowerrc Gruntfile.js  

Общая структура проекта не должна вызывать особых вопросов. Сервисы, директивы, контроллеры, фильтры задаются в соответсвующих файлах, которые потом автоматически подключаются при загрузке приложения:

var angular = require('angular'),         controllers = require('js/controllers'),         services = require('js/services'),         directives = require('js/directives'),         filters = require('js/filters'); ... angular.forEach(services, function (service, name) {         $provide.factory(name, service); }); ...  angular.forEach(directives, function (directive, name) {         app.directive(name, directive); });  angular.forEach(filters, function (filter, name) {         app.filter(name, filter); });  angular.forEach(controllers, function (controller, name) {         app.controller(name, controller); }); 

Я думаю, что многие, да и я сам по началу задавались вопросом, как правильно оформлять логику, которая не привязана к конкретному отображению, а выполняется глобально. Часто ответом на этот вопрос служит контроллер, который вызывается где-то в index.html с помощью ng-controller. Конечно это не смертельно, но я лично считаю такой вариант более правильным:

app.run(controllers.GlobalCtrl); 

То есть данный контроллер вызывается единожды при запуске приложения и формально не связан ни с одним шаблоном.

Одним из приятных дополнений requirejs является модуль text, который позволяет подгружать через require() не только AMD модули, но и обычные текстовые файлы, к примеру, html, которые потом могут быть включены при компиляции. Это позволяет нам добавлять в общий билд файлы шаблонов.

... .when('/', {        controller: controllers.MainCtrl,        template: require('text!templates/main.html') }); ... 

И напоследок, что касается:

$httpProvider.interceptors.push(['$q', '$error', function ($q, $error) {             return {                 'request': function (config) {                     return config;                 },                 'response': function (response) {                     return response;                 },                 'responseError': function (rejection) {                     $error('Connection error!');                     return $q.reject(rejection);                 }              }         }]); 

На мой взгляд посредник при обработке запросов — неотъемлемая часть клиент-серверного взаимодействия, которая позволяет выполнять форматирование ответа или, в конкретном случае, реализовать централизованную обработку всех ошибок.

Не хочу говорить больше, чем нужно, поэтому на этом все. Как и обещал, приглашаю в комментарии, делиться своей практикой, мыслями и предложениями.

ссылка на оригинал статьи http://habrahabr.ru/post/214063/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *