![](http://habrastorage.org/getpro/habr/post_images/69a/dbc/39c/69adbc39c2886a2070b47e1e54f2910a.png)
Мотивация
Свести рутину к минимуму, создать интуитивно понятную архитектуру и собрать вместе то, что называется 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/
Добавить комментарий