Pull to refresh

Comments 24

Реально ли использовать Angular вместе с рендерингом на бекенде?
На mvc я сделал общий view, в котором есть ng-include. Все запросы к любой странице выдают этот view, а он загружает html темплейты в этот ng-include в зависимости от адресной строки. В Angular $location позволяет работать с адресной строкой не перегружая страницу. Такая себе single page app.
Если интересно могу показать код чуть позже, сейчас времени нет.
Расценю плюс как интерес.
Вобщем рецепт:
Делаете общий view и добавляете в него подобный код

<content ng-controller="ContentTemplate">
    <div ng-include="contentUrl"></div>
</content>


В контроллере добавляете вот этот код

$scope.$watch(function () { return $location.path(); }, function (path) {
    var match = path.match(/^\/?([^/?]+)(?:\/([^/?]+))?/i);

    if (match == null) {
        $scope.contentUrl = "Views/Default/Index.html";
        return;
    }

    if (match[2] == null) {
        match[2] = "Index";
    }

    $scope.contentUrl = "/Views/{0}/{1}.html".format(match[1], match[2]);
});


Суть такова: при загрузке любой страницы, в контроллере сервера вы указываете всегда возвращать один и тот же view. Когда он загружается $watch запускается в первый раз, смотрит на адрес, делает простой роутинг и пишет в contentUrl новое значение, которое подхватывает ng-include и загружает новый контент.

Что-бы перейти на другую страницу делаете так:

$location.path("/Controller[/Action]");

$location по умолчанию не перегружает страницу, а только меняет адресную строку. Если браузер не поддерживает такое поведение, то он пишет это как хеш ссылку (address.org#/controller/action) и все тоже работает.
Да можно.
Для подготовки шаблонов можно использовать любой язык и шаблонизатор на Ваш вкус.

Если имелось в виду рендеринг для поисковиков, то тут есть два пути:
  • Использовать prerender.io
  • Генерить статические странички «на лету» или складировать их заранее в кеш.

Мы пытались пойти по первому пути и долго бились, чтобы все заработало как надо. Мы использовали обертку на node.js, но, к сожалению, сам сервер постоянно падал после нескольких десятков обращений. Поэтому мы переключились на второй вариант, на котором и остановились.
А чем второй путь отличается от первого? Чем вы рендерите во втором способе? Собственно вопрос-то в том как не держать второй комплект шаблонов и т.п. на серверной стороне.
Под капотом у prerender'a находится PhantomJS. Он в real-time сходит на страницу и на выходите даст сформированный html с выполненным javascript'ом.

Во втором случае мы просто сделали, как Вы и упомянули, второй комплект шаблонов, который формировали на сервере из данных БД. На этом и остановились, потому как других путей найти не удалось.

Можно немного расширить второй путь и формировать html'ки заранее, складывая их в какой-нибудь кеш. Но тут все зависит от объема данных и насколько актуальными их надо держать. Нам это не подошло, данные могли меняться быстро, а объем позволял их формировать «на лету» без потери производительности.
Тем не менее, важно этим не злоупотреблять, потому что зарегистрировать и получить событие можно в каждом контексте ($scope) любого контроллера.

Добавлю что например для глобальных событий вместо использования $broadcast лучше подписывать события на $rootScope и иницииривать на нем же через $rootScope.$emit. И вообще как по мне оыбчно достаточно глобальны событий, иначе может возникнуть лишняя сложность.
Спасибо, верное замечание, дополню рекомендацию.
Интересно было бы узнать о применении AngularJs для разработки Web-приложений, работающих в браузере мобильных устройств. Как в этом случае трансформируются рекомендуемые ограничения по количеству watch-ей на странице? Было бы здорово еще и узнать о каких-нибудь примерах из реального опыта.
Наше приложение работало также и в мобильных браузерах, и надо отметить это было нашей ошибкой. Вкратце:
  1. Производительность раза в 2-3 (субъективно) хуже, чем на десктопе. У нас использовалась masonry-плитки с картинками.
  2. Пришлось в одном приложении адаптировать пользовательский интерфейс одновременно и для мобильных устройств, и для десктопа. Это вылилось в большую кучу «костылей» и ветвлений в коде, «резиновая» верстка полностью не покрывала все случаи. Чудес, увы, не бывает.
  3. Было очень много багов, связанных с конкретными браузерами на устройствах. Поддерживать их все было очень сложно. Бывали даже случаи, когда один и тот же баг в одной и той же версии браузера повторялся на устройствах Samsung, но упорно не хотел на устройствах LG (Nexus)


Сейчас, мы бы разделили эти приложения на два разных с точки зрения UI части, но объединили бы логическую часть (сервисы) через Ionic. Это было бы правильнее, на мой взгляд.
Но ведь эти проблемы связаны не непосредственно с AngularJs, я правильно понимаю? Т.е. понятно, что резиновый интерфейс не всегда позволяет сделать интерфейс удобным для всех форм-факторов, а косяки с разметкой могут быть разные в разных браузерах. Но если все-таки этот путь выбран (хотя бы как временный), то как себя будет вести AngularJs со своим двусторонним связыванием в мобильном браузере? В аспекте производительности и в аспекте совместимости AngularJs с разными браузерами…
Да, Вы правы, эти проблемы напрямую не относятся к AngularJS. Не могу сказать, что фреймворк накладывал какие-то ограничения на использование в мобильных браузерах конкретно у нас.
Сложновато отделить проблемы с производительностью двойного связывания от наведенных проблем другими компонентами. У нас показывалось примерно до 200 «плиток» на странице (может быть и больше удавалось показывать за счет бесконечной прокрутки, сложно сказать) и на каждой порядка 15 элементов были с двойным связыванием. Хуже всех себя показывал нативный Android браузер. Chrome, Safari и Firefox были примерно на одном уровне. Были небольшие тормоза при прокрутке и «затыки» при добавлении новых элементов, вероятно связанные в большей степени из-за манипуляций с расчетом позиций этих «плиток».
В целом, не могу сказать что производительность страдала конкретно из-за AngularJS. При разумном применении (не на синтетическом примере в 2000 $watch), я думаю скорость не сильно упадет.

По совместимости, могу сказать, что не сталкивался с проблемами. Во всех браузерах логический код отрабатывал ожидаемо.
Спасибо!
Еще попутно вопрос по структурированию кода приложения: где, по Вашему мнению, лучше размещать логику построения (перестроения) DOM, общую для нескольких директив? Ведь наследования директив нормальными способами не добиться?
А можете привести пример такой логики? Не встречал такой, если честно…
Наследования как такового конечно же нет, но есть очень полезная штука require. С ее помощью можно логически объединять директивы, зависящие друг от друга. Например, если Вы захотели сделать собственный dropdown, то можно директиву разбить на две части. Одна часть будет связана с логикой «навешивания» на элемент (и таких директив может быть несколько, в зависимости от элемента, например), а вторая часть будет реализовывать показ непосредственно панели со списком.
Такой подход позволит использовать общую часть (панель со списком) с разными элементами и директивами (меню или инпут с селектом).
Например, я хочу несколько директив, которые по сути своей dropdown-контролы, но содержимое выпадающей области разное: в одном случае простой список, в другом — какая-то более ложная конструкция, позволяющая искать/выбирать элементы. В силу того, что большую часть времени я пишу на языке с наследованием, мне описанное разнообразие хочется сделать с помощью соответствующих классов-наследников. Хотя, require, похоже тоже для этого подойдет.
Насколько Angular подходит для написания приложения с не очень сложной svg графикой в дуэте со Snap.svg, например? Или для подобных приложений он не нужен вовсе?
Хотя у меня и не было опыта работы с Snap.svg, но мне кажется, если у Вас просто манипуляция без сложной логики, то Angular тут будет не нужен.
На момент старта нашего проекта, такой штуки не было, и поэтому все привыкли описывать зависимости инъекций руками.
Добавлю в полезные инструменты.
В целом такой подход (анонимные функции) объявления модулей вообще не рекоммендуется. Лучше объявлять именованные функции и их зависимости через $inject или ng-annotate (делает тот же $inject). В моем случае все моудли разбиты на отдельные файлы и линкуются через browserify, именем модуля является имя функции, довольно удобно.
В целом такой подход (анонимные функции) объявления модулей вообще не рекоммендуется.

А почему не рекомендуется? Не встречал такого мнения…

В моем случае все моудли разбиты на отдельные файлы и линкуются через browserify, именем модуля является имя функции, довольно удобно.

У Вас в одном файле собраны все сервисы, контроллеры и директивы, связанные с модулем?
Мы разбивали каждую отдельную сущность на отдельный файл. Структура слегка «пухла», но в целом было удобнее вносить точечные правки и рефакторить файл, было меньше конфликтов.
А почему не рекомендуется? Не встречал такого мнения…

Хотя бы потому что анонимную функцию не получится вынести в отдальный файл, что желательно как по мне. Проще читается (особенно когда все разнесено по отдельным файлам). Отлаживать проше и прочее. Здесь еще можно почитать github.com/johnpapa/angular-styleguide

У Вас в одном файле собраны все сервисы, контроллеры и директивы, связанные с модулем?

Да, модуль который просто все связывает и больше ничего не делает. Примерно так:
  1. 'use strict';
  2.  
  3. var dependencies = [];
  4.  
  5. var controller = require('./profile-controller');
  6. var profilePreview = require('./profile-preview-directive');
  7.  
  8. // blocks
  9. var boxDirective = require('./blocks/profile-box-directive');
  10. var connectLinksDirective = require('./blocks/profile-connect-lInks-directive');
  11. var exploreDirective = require('./blocks/profile-explore-directive');
  12. var groupsDirective = require('./blocks/profile-groups-directive');
  13. var summaryDirective = require('./blocks/profile-summary-directive');
  14. var skillsDirective = require('./blocks/profile-skills-directive');
  15. var experienceDirective = require('./blocks/profile-experience-directive');
  16. var educationDirective = require('./blocks/profile-education-directive');
  17.  
  18. module.exports = angular.module('app.profile', dependencies)
  19.     .controller(controller.name, controller)
  20.     .directive(profilePreview.name, profilePreview)
  21.  
  22.     // blocks
  23.     .directive(boxDirective.name, boxDirective)
  24.     .directive(connectLinksDirective.name, connectLinksDirective)
  25.     .directive(exploreDirective.name, exploreDirective)
  26.     .directive(groupsDirective.name, groupsDirective)
  27.     .directive(skillsDirective.name, skillsDirective)
  28.     .directive(summaryDirective.name, summaryDirective)
  29.     .directive(experienceDirective.name, experienceDirective)
  30.     .directive(educationDirective.name, educationDirective);
  31.  
PS я больше не использую подход function.name очевидно в этом случае имена функций ужимать нельзя при минификации, использую кастомное статическое поле $name.
Эка…
$scope.dataLoaded.then(handler)

будет срабатывать каждый раз при переписывании dataLoaded?

Оно же просто добавит handler к тому промизу, на который в данный момент ссылается dataLoaded.
Sign up to leave a comment.