Comments 45
Спасибо за статью, тема действительно интересна!
Кстати, angular очень хорошо подходит для написания сложных браузерных плагинов.
Документация, в самом деле, никуда не годна. Приходится собирать информацию по крупицам.
Кстати, angular очень хорошо подходит для написания сложных браузерных плагинов.
Документация, в самом деле, никуда не годна. Приходится собирать информацию по крупицам.
Задолбал всех, наверное, со своей рекламой, но, пожалуйста, перевод документации: angular.ru Скоро добью API. Переводите учебник, допиливайте существующие переводы, присылайте свои примеры: angular.ru/cookbook/. Я, когда разберусь с какой-нибудь штукой трачу 30 мин. и пишу пример. Полнота документации зависит в том числе от вас.
Обязательно пишите еще.
По документации на самом деле быстро вникнуть и проникнуться сложновато (особенно после нескольких лет работы с jQuery и ей подобными).
По документации на самом деле быстро вникнуть и проникнуться сложновато (особенно после нескольких лет работы с jQuery и ей подобными).
На документацию по AngularJS грех жаловаться. На официальном сайте её много. От туториалов, до продвинутых сценариев с примерами. Одни юнит тесты чего стоят! Если нужно что-то большее — есть поисковики и исходники.
Вот чего действительно не хватает, так это альтернативы директивам. Простенькие или многостраничные сайты на директивах нормально пишутся, но если заходит речь о динамических single-page приложениях, то с директивами тут трешак начинается. Очень неудобно с ними работать, особенно после jquery виджетов.
Вот чего действительно не хватает, так это альтернативы директивам. Простенькие или многостраничные сайты на директивах нормально пишутся, но если заходит речь о динамических single-page приложениях, то с директивами тут трешак начинается. Очень неудобно с ними работать, особенно после jquery виджетов.
Почему с директивами неудобно получается? Я сейчас в свободное время изучаю потихоньку ангуляр, и именно директивы кажутся мне очень удобными.
Мне очень нравится модульность Angular'a. В рамках же single-page приложения очень хочется избежать постоянной подгрузки темплейтов с сервера. А ввиду большого количества темплейтов, крайне не хочется хранить все темплейты в одной куче в рутовом index.html, а перенести их в конкретные модули. С простыми темплейтами нет проблем, но как всегда хочется большего. Нужна интернализация, автоматическая подстановка значений из разных scope'ов, быстрая динамическая перестройка html. Первого в angular'е нет, но можно самому написать через директивы; подстановка работает с некоторыми ограничениями; а вот динамически менять dom достаточно муторно. По сути, нужно каждый раз компилировать темплейты, чтобы работали подстановки и выполнялись директивы. Наверно тут нужно определенное мастерство, т.к. то же самое на jquery виджетах делается в 10 раз быстрее и занимает меньше кода. В идеале, возможно было бы здорово совместить jquery виджеты (UI) с angular'овскими модулями (logic), но что-то по-простому это не сращивается.
Не знаю что бы такого в качестве примера привести… За полгода уже подзабылось малость. Ниже идет простенькая директива с кнопками OpenID провайдеров. По сути, приходится темплейты описывать конкатенацией строк, т.е. все описывается декларативно. Если нужно динамически менять какой-нибудь css аттрибут — в темплейт добавляется директива. Если нужен action — добавляется директива. Нужна интернализация — добаляется директива. Форматирование? — директива итд. Так код очень быстро сильно захламляется ненужным синтаксисом. Если же нужно вставить новый блок с другим виджетом динамически… Тут уже траблы. В общем, чем больше виджеты, тем сложнее с ними работать ;)
Если кто подскажет как правильно и эффективно работать с директивами — буду только рад.
Не знаю что бы такого в качестве примера привести… За полгода уже подзабылось малость. Ниже идет простенькая директива с кнопками OpenID провайдеров. По сути, приходится темплейты описывать конкатенацией строк, т.е. все описывается декларативно. Если нужно динамически менять какой-нибудь css аттрибут — в темплейт добавляется директива. Если нужен action — добавляется директива. Нужна интернализация — добаляется директива. Форматирование? — директива итд. Так код очень быстро сильно захламляется ненужным синтаксисом. Если же нужно вставить новый блок с другим виджетом динамически… Тут уже траблы. В общем, чем больше виджеты, тем сложнее с ними работать ;)
Если кто подскажет как правильно и эффективно работать с директивами — буду только рад.
angular.module('sipSsoLogin.directive', [ 'sipCommonI18n', 'sipSsoLogin.controller' ]).directive('sipSsoLogin', function () {
var buttonTemplate = '<button id="sipSsoProvider_%PROVIDER%" ng-click="onProviderSelected(\'%PROVIDER%\')"><div class="sipSsoProvider sipSsoProvider_%PROVIDER%"></div></button>';
function getProviderButtonTemplate(provider) {
return buttonTemplate.split("%PROVIDER%").join(provider);
};
return {
restrict: 'E',
template:
'<div ng-controller="sipSsoLogin">' +
'<div class="sipSsoPanel_text" sip-common-i18n="sso.login.directive" />' +
'<div>' +
getProviderButtonTemplate('google') +
getProviderButtonTemplate('yahoo') +
getProviderButtonTemplate('yandex') +
getProviderButtonTemplate('twitter') +
'</div><div>' +
getProviderButtonTemplate('openid') +
getProviderButtonTemplate('myopenid') +
getProviderButtonTemplate('facebook') +
getProviderButtonTemplate('vkontakte') +
'</div><div class="sipSsoPanel_inputContainer">' +
'<button style="float: right; margin: 0px 10px 0px 10px;" sip-common-i18n="sso.button.login"></button>' +
'<div style="overflow: hidden;" class="sipSsoPanel_inputField">' +
'<span style="float: left; padding-right: 5px; vertical-align: middle;">{{prefix}}</span>' +
'<div style="overflow: hidden">' +
'<input style="display: inline-block; width: 100%;" type="text" value="{{user}}">' +
'</div>' +
'</div>' +
'</div>',
replace: true
};
});
Жесть. Надеюсь, что эта куча говно-кода этот пример является просто примером:
1. Необходимо всю разметку перенести в отдельный файл-шаблон и заменить опцию template на templateUrl.
2. Удалить функцию getProviderButtonTemplate и воспользоваться директивой ангуляра ng-repeat
3. Убрать inline-стили. Но это так — общий совет.
Получится что-то вроде такого:
код:
loginPanel.html
С этим уже намного приятнее работать и можно двигаться дальше:
4. Директива 'sipSsoLogin.directive' — не нужна вообще (у неё нет функционала кроме замены шаблона). Удалить её и перейти на директиву ng-include которая реализует подобный функционал.
5.… у нас остаётся только разметка, в которой кстати я вижу кастомную интернациализацию. В Ангуляре есть собственные средства интернационализации. Необходимо ими воспользоваться и, подозреваю, что уйдёт ещё кучаговно кода
1. Необходимо всю разметку перенести в отдельный файл-шаблон и заменить опцию template на templateUrl.
2. Удалить функцию getProviderButtonTemplate и воспользоваться директивой ангуляра ng-repeat
3. Убрать inline-стили. Но это так — общий совет.
Получится что-то вроде такого:
код:
angular.module('sipSsoLogin.directive', [ 'sipCommonI18n', 'sipSsoLogin.controller' ]).directive('sipSsoLogin', function () {
return {
restrict: 'E',
templateUrl: 'loginPanel.html',
replace: true
};
});
loginPanel.html
<div ng-controller="sipSsoLogin">
<div class="sipSsoPanel_text" sip-common-i18n="sso.login.directive" />
<div ng-repeat="prov in providers">
<button id="{{prov.sipSsoProviderId" ng-click="onProviderSelected(prov)">
<div ng-class="prov.sipSsoProvider"></div>
</button>
</div>
<div class="sipSsoPanel_inputContainer">
<button class="..." sip-common-i18n="sso.button.login"></button>
<div class="..." class="sipSsoPanel_inputField">
<span class="...">{{prefix}}</span>
<div class="...">
<input type="text" value="{{user}}">
</div>
</div>
</div>
</div>
С этим уже намного приятнее работать и можно двигаться дальше:
4. Директива 'sipSsoLogin.directive' — не нужна вообще (у неё нет функционала кроме замены шаблона). Удалить её и перейти на директиву ng-include которая реализует подобный функционал.
<div ng-include="'loginPanel.html'"></div>
5.… у нас остаётся только разметка, в которой кстати я вижу кастомную интернациализацию. В Ангуляре есть собственные средства интернационализации. Необходимо ими воспользоваться и, подозреваю, что уйдёт ещё куча
Не беспокойся, это в качестве примера. Inline стили тоже для дебага были. Потом все стили плавно переползают в less.
Вот чего хочется избежать, так это:
Темплейтов куча. Делать кучу запросов к серверу за темплейтами не хочется. Держать темплейты в одном месте тоже. Как быть?
Интернационализации в angular'е нет.
Вот чего хочется избежать, так это:
1. Необходимо всю разметку перенести в отдельный файл-шаблон и заменить опцию template на templateUrl.
Темплейтов куча. Делать кучу запросов к серверу за темплейтами не хочется. Держать темплейты в одном месте тоже. Как быть?
Интернационализации в angular'е нет.
Все шаблоны должны быть в отдельных файлах. Не хочу даже обсуждать это. Откуда-то с Хабра: (с) Мы пишем код для людей, а не машин.
По поводу требования о загрузке шаблонов. Оно разумно и мне вполне понятно. Смею предложить следующий алгоритм:
1. При сборке проекта все файлы шаблонов объединяются в один (и минимизируются для оптимизации).
2. Файл с шаблонами загружается в главном контроллёре, скажем так:
Контроллёр загружает файл с шаблонами, парсит его (разбивает на составные части) и «накачивает» ими Ангуляровский $templateCache, который использует директива ng-include (необходимо только выяснить в коде ангуляра, код кэша).
Таким образом и код будет выглядеть стандартно и подгрузки шаблонов не будет.
По поводу требования о загрузке шаблонов. Оно разумно и мне вполне понятно. Смею предложить следующий алгоритм:
1. При сборке проекта все файлы шаблонов объединяются в один (и минимизируются для оптимизации).
2. Файл с шаблонами загружается в главном контроллёре, скажем так:
<body ng-controller="mainController">
...
</body>
Контроллёр загружает файл с шаблонами, парсит его (разбивает на составные части) и «накачивает» ими Ангуляровский $templateCache, который использует директива ng-include (необходимо только выяснить в коде ангуляра, код кэша).
Таким образом и код будет выглядеть стандартно и подгрузки шаблонов не будет.
Спасибо, что то похожее думалось сделать, но времени не хватило. Попробую еще раз взяться за angular при первой возможности.
можно использовать grunt-angular-templates для сборки темплейтов в один пекедж — и не надо ничего подгружать на ходу
Вероятно я Вас не правильно понимаю насчёт интернационализации, но разве это не оно? I18n and L10n in AngularJS
Базового функционала явно не хватает. Поэтому народ жалуется на i18n/l10n и пишет свои сервисы, модули итд.
Это да. Недавно натолкнулся на хороший проект angular-translate, рекомендую.
Воспользуюсь случаем пропиарить свой angular-l10n
Не копался еще в исходниках. Интересует смена языков. Есть возможность подгружать переводы аяксом, а так же при необходимости не заменять старые переводы? Т.е. чтобы сразу оригинал и перевод показывались?
загрузку ajax-ом не делал — придерживаюсь подхода, что приложение должно быть упаковано целиком, для доставки на клиент. В целом ничего не мешает сделать загрузку через $http — модуль к этому готов.
Отображать одновременно строки из нескольких переводов? Технически возможно, но зачем?
Отображать одновременно строки из нескольких переводов? Технически возможно, но зачем?
Для админки. Обычные пользователи всегда получают только данные для одного языка, админы и переводчики сразу все, чтобы в редакторе можно было сопоставлять оригинальный текст переводу.
Не очень понятен подход про упаковку. Т.е. для обычных пользователей должны подгружаться интерфейсы админки?
Не очень понятен подход про упаковку. Т.е. для обычных пользователей должны подгружаться интерфейсы админки?
Такого функционала нет и он не нужен в составе основной библиотеки, потому что это уже не локализация.
Можно написать сервис l10n-manage который позволит получить доступ ко всем переводам одновременно и работать с ними как с данными.
Можно написать сервис l10n-manage который позволит получить доступ ко всем переводам одновременно и работать с ними как с данными.
У рельсового sprockets есть такая удобная фича как JST, смысл в том что все темплейты собираются в глобальной переменной и их удобно вытаскивать в любом месте, например
template: JST[«test/template»]().
Я для этого написал пару директив аля jst-include, в итоге все нужные темплейты загружаются 1 раз, и при этом не представляют собой одностраничной мешанины.
Посмотрите, может в Вашем случае тоже есть подобное решение.
template: JST[«test/template»]().
Я для этого написал пару директив аля jst-include, в итоге все нужные темплейты загружаются 1 раз, и при этом не представляют собой одностраничной мешанины.
Посмотрите, может в Вашем случае тоже есть подобное решение.
Мне кажется что директивы как раз таки снимают проблему масштабирования приложений. У меня во всех проектах все разделено на модули, большую часть которых можно реюзать на других проектах. Модули включают в себя сервисы, директивы… От их количества мне ни холодно ни жарко, ибо они никак друг с другом не связаны и используются только там где надо.
А какие проблемы с директивами, какой трешак? Ну, работы чуть больше выходит, да, но вот серьезных проблем при полностью правильном подходе не обнаруживал. Ну и да, ангуляр-то предназначен как раз для сингл-пейдж приоложений, не для простеньких сайтов.
Наверное имеются ввиду проблемы совмещения директив и jquery-плагинов.
Т.к. сами по себе директивы — это просто сказка
Т.к. сами по себе директивы — это просто сказка
Кстати, разработчики честно пишут, что AngularJS хорош именно для CRUD-приложений. Игры или насыщенные DOM-манипуляциями приложения предлагают писать на jQuery. А преимущество директив мне видится в том, что они в разы повышают модульность, поэтому подключать сторонние плагины не сложнее чем в jQuery: $(elem).jqplugin, <div angular-plugin></div>
по поводу такой записи:
Такие конструкции любят съедать минификаторы (уже набил шишки:)). Лучше писать так:
или прописывать зависимости через inject:
function forExampleController($scope) {
$scope.word="Habrahabra";
$scope.log="";
}
Такие конструкции любят съедать минификаторы (уже набил шишки:)). Лучше писать так:
angular.module('app').controller('SomeCtrl', ['$scope', function ($scope) {
...
или прописывать зависимости через inject:
SomeCtrl.$inject = ['$scope'];
Согласен, со временем я пришел к такого вида конструкции (видимо Java аннотации перед объявлением классов и методов оказали на меня влияние):
(function () {
angular.module('myModule').directive('myDirective', MyDirective)
MyDirective.$inject = ['$scope', '$http']
function MyDirective ($scope, $http) {
// bla bla bla
}
})()
Хороший материал, однако считаю, что в любой туториал по AngularJS хорошо бы добавлять ссылку на замечательные скринкасты от John Lindquist egghead.io
И ссылку на хорошую блогозапись о написании и тестировании директив: blog.angularjs.org/2012/11/about-those-directives.html
Очень много хабрабры, рябит в глазах. Может лучше более осмысленные имена придумать? «Hello world», например. А лучше «Привет, мир», чтобы данные лучше выделялись среди кода на латинице :-)
Так же стоит упомянуть вначале, что attrs.habraHabr преобразуется соответственно из habra-habr, habra:habr и т.д.
Написать строчку комментария про $watch. Для новичков же статья
Непонятно, зачем писать «внутри директивы interpolate "{{}}"», если проще написать: внутри выражения "{{}}"
Почему второй пример записывается так:
а не так, в соответствии с приведенным выше расширенным описанием директивы
Не говорю, что это не правильно, просто без объяснений поменяли логику.
Не очень понято с pre и post. Можно постараться доходчивее объяснить.
Сам в сомнениях, но стоит ли переводить link fn как связующую функцию? Пока перевожу как функция линковки, и сам процесс link как линковку. Не совсем по-русски, но связывание уже употребляется при описании дата-биндинга и могут быть разночтения.
За статью, конечно, спасибо. Пишите еще обязательно!
Так же стоит упомянуть вначале, что attrs.habraHabr преобразуется соответственно из habra-habr, habra:habr и т.д.
Написать строчку комментария про $watch. Для новичков же статья
Непонятно, зачем писать «внутри директивы interpolate "{{}}"», если проще написать: внутри выражения "{{}}"
Почему второй пример записывается так:
compile: function compile(templateElement, templateAttrs) {
templateElement.html("<div>{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+"</div>");
return function (scope, element, attrs) {
}
}
а не так, в соответствии с приведенным выше расширенным описанием директивы
compile: function compile(temaplateElement, templateAttrs) {
templateElement.html("<div>{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+"</div>");
},
link: function (scope, element, attrs) {
}
Не говорю, что это не правильно, просто без объяснений поменяли логику.
Не очень понято с pre и post. Можно постараться доходчивее объяснить.
Сам в сомнениях, но стоит ли переводить link fn как связующую функцию? Пока перевожу как функция линковки, и сам процесс link как линковку. Не совсем по-русски, но связывание уже употребляется при описании дата-биндинга и могут быть разночтения.
За статью, конечно, спасибо. Пишите еще обязательно!
Спасибо за замечания и предложения
Обязательно расскажу про нормализацию атрибутов и форматы задания директив в заключительной части. Опустил это вначале из тех соображений, что это вопросы скорее относятся к стилю оформления кода, а не к самой концепции директив, и в самом начале особой пользы для понимания в них я не увидел.
Да, тоже хотелось уйти от этого, но лучшей альтернативы для того, чтобы показать, как можно при инициализации директивы передавать в нее имя переменной в scope контролера и использовать ее, я не придумал.
Хотел акцентировать внимание на том, что все «выражения», которые пишутся в разметке есть директивы. Возможно слишком назойливо.
Согласен, внес изменения.
преобразуется соответственно из habra-habr, habra:habr и т.д.
Обязательно расскажу про нормализацию атрибутов и форматы задания директив в заключительной части. Опустил это вначале из тех соображений, что это вопросы скорее относятся к стилю оформления кода, а не к самой концепции директив, и в самом начале особой пользы для понимания в них я не увидел.
Написать строчку комментария про $watch. Для новичков же статья
Да, тоже хотелось уйти от этого, но лучшей альтернативы для того, чтобы показать, как можно при инициализации директивы передавать в нее имя переменной в scope контролера и использовать ее, я не придумал.
Непонятно, зачем писать «внутри директивы interpolate "{{}}"», если проще написать: внутри выражения "{{}}"
Хотел акцентировать внимание на том, что все «выражения», которые пишутся в разметке есть директивы. Возможно слишком назойливо.
Почему второй пример записывается так:
compile: function compile(templateElement, templateAttrs) { templateElement.html("{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+""); return function (scope, element, attrs) { } }
а не так, в соответствии с приведенным выше расширенным описанием директивы
compile: function compile(temaplateElement, templateAttrs) { templateElement.html("{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+""); }, link: function (scope, element, attrs) { }
Согласен, внес изменения.
Некоторые вещи лучше вначале не опускать, потому что у человека затык будет, если он сразу не поймет откуда что взялось. Можно хотя бы в скобочках кратко (habraHabr эквивалентен habra-habr из атрибута). Пусть упрощенно, главное, чтобы человек понял что это не ошибка/опечатка/выдумка автора, а так и должно быть и пошел бы дальше.
Про $watch то же самое. Незнакомая непонятная функция, опять затык. А так ($watch следит за изменениями переменных). Можно и подробнее расписать. Хоть целый абзац ввести. Если он будет помогать понять логику, то чтение пойдет быстрее, т.к. не придется задумываться над непонятными вещами.
С "{{}}" тогда уж лучше дописать строчку где отчетливо акцентировать внимание на этом. Например, «… внутри выражения "{{}}" (или внутри любой другой, встроенной в наш элемент директивы)», а то получается ни рыба ни мясо :)
Про $watch то же самое. Незнакомая непонятная функция, опять затык. А так ($watch следит за изменениями переменных). Можно и подробнее расписать. Хоть целый абзац ввести. Если он будет помогать понять логику, то чтение пойдет быстрее, т.к. не придется задумываться над непонятными вещами.
С "{{}}" тогда уж лучше дописать строчку где отчетливо акцентировать внимание на этом. Например, «… внутри выражения "{{}}" (или внутри любой другой, встроенной в наш элемент директивы)», а то получается ни рыба ни мясо :)
не туда написал, см. выше
UFO just landed and posted this here
Sign up to leave a comment.
Директивы в Angularjs для начинающих. Часть 1