Использование angularjs с турболинками

Я пытаюсь использовать фреймворк Angularjs в своем приложении с турболинками. После изменения страницы он не инициализирует новые прослушиватели событий. Есть ли способ заставить его работать? Заранее спасибо!


person Pavel    schedule 10.02.2013    source источник
comment
Было бы неплохо использовать турболинки с инфраструктурой MVC на клиенте. Разве турбоссылки не о сокращении Javascript до минимума, чтобы получить преимущество кодирования только на Ruby для обслуживания клиентских страниц, а также запуска серверного кода?   -  person sudhanshu    schedule 27.02.2013


Ответы (9)


AngularJS против Turbolinks

Turbolinks, а также AngularJS можно использовать для ускорения отклика веб-приложения в том смысле, что в ответ на действия пользователя что-то происходит на веб-странице без перезагрузки и перерисовка всей страницы.

Они различаются в следующем отношении:

  • AngularJS помогает вам создавать многофункциональное клиентское приложение, где вы пишете много кода JavaScript, который выполняется на клиентском компьютере. Этот код делает сайт интерактивным для пользователя. Он взаимодействует с бэкендом на стороне сервера, то есть с приложением Rails, используя JSON API.

  • Turbolinks, с другой стороны, помогает сделать сайт интерактивным, не требуя написания кода JavaScript. Это позволяет вам придерживаться кода Ruby/Rails, работающего на стороне сервера, и по-прежнему «волшебным образом» использовать AJAX для замены и, следовательно, повторного рендеринга только тех частей страницы, которые были изменены.

Хотя Turbolinks силен тем, что позволяет вам использовать этот мощный механизм AJAX, ничего не делая вручную и просто кодируя Ruby/Rails, по мере роста вашего приложения может наступить этап, когда вы захотите интегрировать JavaScript-фреймворк, такой как AngularJS.

Особенно на этом промежуточном этапе, когда вы хотите последовательно интегрировать AngularJS в свое приложение, по одному компоненту за раз, имеет смысл запускать Angular JS и Turbolinks вместе.

Как использовать AngularJS и Turbolinks вместе

Используйте обратный вызов для ручной загрузки Angular

В вашем коде Angular у вас есть строка, определяющая ваш модуль приложения, примерно так:

# app/assets/javascripts/angular-app.js.coffee
# without turbolinks
@app = angular.module 'MyApplication', ['ngResource']

Этот код запускается при загрузке страницы. Но поскольку Turbolinks просто заменяет часть страницы и предотвращает загрузку всей страницы, вы должны убедиться, что угловое приложение правильно инициализировано («загрузилось»), даже после таких частичных перезагрузок, выполненных Turbolinks. Таким образом, замените приведенное выше объявление модуля следующим кодом:

# app/assets/javascripts/angular-app.js.coffee
# with turbolinks
@app = angular.module 'MyApplication', ['ngResource']

$(document).on 'turbolinks:load', ->
  angular.bootstrap document.body, ['MyApplication']

Не загружайтесь автоматически

Вы часто видите в руководствах, как автоматически загружать приложение Angular с помощью атрибута ng-app в вашем HTML-коде.

<!-- app/views/layouts/my_layout.html.erb -->
<!-- without turbolinks -->
<html ng-app="YourApplication">
  ...

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

Таким образом, просто удалите этот атрибут ng-app:

<!-- app/views/layouts/my_layout.html.erb -->
<!-- with turbolinks -->
<html>
  ...

Дальнейшее чтение

person fiedl    schedule 18.03.2013
comment
А попробовал метод начальной загрузки. Однако я столкнулся с проблемой, когда в моих директивах теперь создаются несколько событий. Например, если turbolinks загружает представление с помощью: ng-click="save()", а затем перезагружает представление... мой метод save() теперь срабатывает три раза. Есть идеи? - person BradGreens; 18.07.2013
comment
Я смог решить проблемы с несколькими событиями и, возможно, другие сценарии с ошибками, переместив Angular в раздел <head> нашего администратора (таким образом, предотвратив несколько экземпляров моих скриптов). Это не публичный опыт, поэтому меня не слишком беспокоят подводные камни загрузки JS в разделе заголовка html. - person BradGreens; 18.07.2013
comment
Этот метод отлично работал у меня с angularjs 1.0.6. Но после обновления до angularjs 1.2.0.rc3 я получаю это в консоли после перехода на новую страницу через турбоссылки: Uncaught Error: [ng:btstrpd] App Already Bootstrapped with this Element 'document'. По-видимому, элемент может быть загружен только один раз. Изменение angular.bootstrap(document, ['YourApplication']) на angular.bootstrap("body", ['YourApplication']) работает без проблем. - person nates; 31.10.2013
comment
Мне пришлось использовать body, но также пришлось удалить ready из строки $(document).on и добавить вместо нее $ -> - person Chris Edwards; 18.03.2014
comment
Я использовал «тело», но добавил unless $('body').hasClass('ng-scope') (coffeescript!) После angular.bootstrap - person rweng; 08.04.2014
comment
Отличный ответ! При использовании Turbolinks 5 имя события необходимо изменить на $(document).on "turbolinks:load", -> …. Спасибо! - person Repolês; 11.07.2016
comment
Не сработало, и почему вы используете coffeescript вместо js? Для общего использования предпочтительнее использовать javascript, поэтому мне не нужно его конвертировать. - person Joe B.; 09.08.2016
comment
Подтверждена работа над Rails/Turbolinks 5 и Angular 1.5.8 с использованием $(document).on('turbolinks:load', function() { angular.bootstrap(document.body, ['myApp']); }) Спасибо! - person Ryan; 13.08.2016
comment
Я обновил ответ для rails 5 (turbolinks 5) и angular 1.5.8 и добавил демонстрационное приложение на github. Не стесняйтесь проверять коммиты, чтобы увидеть отдельные шаги. - person fiedl; 19.08.2016
comment
Я только что столкнулся с проблемой с этим методом. У меня был интервал, работающий в службе, и это заставляло его запускать новый интервал при каждой загрузке страницы, а старые не умирали, заставляя их складываться. Придумал очень грязное решение, но хотел бы услышать любые другие идеи. stackoverflow.com/questions/41206111/turbolinks-with-angular/ - person Ryan; 18.12.2016

Турболинки пытаются оптимизировать отрисовку страниц и конфликтуют с обычной загрузкой AngularJS.

Если вы используете Turbolinks в некоторых местах вашего приложения, а некоторые части используют Angular. Я предлагаю это элегантное решение:

Каждая ссылка на страницу angularapp (где вы используете ng-app="appname") должна иметь этот атрибут:

<a href="/myapp" data-no-turbolink>Say NO to Turbolinks</a>.

Второй, упомянутый в Stackoverflow, явно перезагружает/загружает каждое ng-приложение, обрабатывая событие page:load. Я бы хотел, чтобы это было навязчиво, не говоря уже о том, что вы потенциально загружаете что-то, чего нет на странице, поэтому тратите ресурсы впустую.

Я лично использовал вышеуказанное решение.

Надеюсь, поможет

person jeveloper    schedule 11.08.2014
comment
Это полезно в очень большом приложении, чтобы свести к минимуму конфликт - person wukong; 03.11.2014
comment
Ценим обратную связь :) - person jeveloper; 04.11.2014

В случае ошибки

Неперехваченная ошибка: [ng:btstrpd] Приложение уже загружено с этим элементом «документ»

после обновления до angular 1.2.x вы можете использовать ниже, чтобы решить проблему.

angular.module('App').run(function($compile, $rootScope, $document) {
  return $document.on('page:load', function() {
    var body, compiled;
    body = angular.element('body');
    compiled = $compile(body.html())($rootScope);
    return body.html(compiled);
  });
});

В предыдущем посте @nates предложил изменить angular.bootstrap(document, ['YourApplication']) на angular.bootstrap("body", ['YourApplication']), но это вызывает вспышку нескомпилированного контента.

person Artur Trzop    schedule 12.01.2014

Turbolinks не совсем подходит для клиентской среды MVC. Turbolinks используется для удаления из ответа сервера всего, кроме тела. С MVC на стороне клиента вы должны просто передавать JSON клиенту, а не HTML.

В любом случае, turbolinks создает свои собственные обратные вызовы.

page:load
page:fetch
page:restore
page:change
person cpuguy83    schedule 10.02.2013
comment
Я использую angular js только на нескольких страницах своего приложения. И я думал, что есть способ сделать это, не отказываясь от турболинков. - person Pavel; 11.02.2013
comment
Я пытался подключиться к обратным вызовам, это вызывает проблемы с window.history и местоположением. Мне трудно сформулировать их, но я могу сказать вам, что реализация нетривиальна, и отключение турболинков решает проблему, ref: stackoverflow.com/questions/30804236/ - person dps; 18.06.2015

Плагин jquery.turbolinks может инициировать загрузку модулей с помощью директив ng-app. Если вы пытаетесь вручную загрузить свои модули, jquery.turbolinks может привести к ошибкам ng:btstrpd. Одно предостережение, которое я обнаружил, заключается в том, что jquery.turbolinks зависит от события page:load, которое может срабатывать до того, как любые новые теги <script> для конкретной страницы завершат работу. Это может привести к $injector:nomod ошибкам, если вы включите определения модулей вне application.js. Если вы действительно хотите, чтобы ваши модули были определены в отдельных файлах javascript, которые включены только на определенные страницы, вы можете просто отключить турбоссылки для любых ссылок на эти страницы через data-no-turbolink.

person Gabe Martin-Dempesy    schedule 08.01.2014
comment
Я должен был прочитать это ближе, прежде чем публиковать, это, по крайней мере, частично отвечает на мой вопрос в посте. Есть ли что-нибудь еще полезное, что нужно знать о том, как работает Turbolinks, когда у меня есть приложение, которое использует внешний фреймворк? - person josiah; 23.02.2015

Добавьте в приложение следующий обработчик событий.

Кофейный скрипт:

bootstrapAngular = ->
  $('[ng-app]').each ->
    module = $(this).attr('ng-app')
    angular.bootstrap(this, [module])

$(document).on('page:load', bootstrapAngular)

Javascript:

function bootstrapAngular() {
  $('[ng-app]').each(function() {
    var module = $(this).attr('ng-app');
    angular.bootstrap(this, [module]);
  });
};
$(document).on('page:load', bootstrapAngular);

Это приведет к тому, что приложение angular будет запускаться после каждой страницы, загруженной Turbolinks.

Кредит https://gist.github.com/ayamomiji/4736614

person Robin Daugherty    schedule 03.10.2013
comment
Действительно трудно принять Coffeescript! Версия без кофе для меня намного чище. - person penner; 20.12.2013
comment
Это работает для меня, за исключением случаев, когда вы переходите на страницу напрямую. отстой! - person Allen; 11.02.2014
comment
Да, чтобы быть более ясным, это относится только к страницам, загруженным Turbolinks. Вы также можете включить $(document).on('ready', bootstrapAngular). - person Robin Daugherty; 11.02.2014
comment
Нет, я тебя ВСЕХ побил. Вы ДОЛЖНЫ прослушивать готовность и страницу: загрузить, НО вы НЕ должны использовать ng-app. Когда angular увидит ng-app, он попытается загрузить ваш модуль, но слишком рано. Используйте другой настраиваемый атрибут, например, my-app вместо ng-app, чтобы избежать этой проблемы. Моя проблема заключалась в том, что он пытался загрузить мой модуль ДО того, как предыдущий код действительно загрузился в мой модуль. - person Allen; 11.02.2014
comment
Это работает лучше, чем принятое решение. Вы получаете минус за то, что не используете javascript, и плюс за правильное решение. - person Joe B.; 09.08.2016

Основываясь на комментариях, которые я видел, единственный допустимый сценарий использования обоих вместе таким образом, что Angular будет конфликтовать с Turbolinks (например, когда я разрешаю Angular обрабатывать часть маршрутизации), — это если у меня есть существующее приложение, которое я m пытаюсь портировать на Angular.

Лично, если бы я делал это с нуля, я думаю, что лучшим решением было бы решить, что должно обрабатывать маршрутизацию, и придерживаться этого. Если Angular, то избавьтесь от Turbolinks -> это мало что даст вам, если у вас есть что-то близкое к одностраничному приложению. Если вы позволяете Rails обрабатывать маршрутизацию, то просто используйте Angular для организации поведения на стороне клиента, которое не может быть обработано сервером при обслуживании шаблонов.

Я пропустил сценарий, здесь? Мне не кажется элегантным пытаться разделить обязанности по маршрутизации между разными фреймворками, даже в большом приложении... Есть ли какой-нибудь другой сценарий, в котором Turbolinks будет мешать Angular, кроме обновления страницы или перехода к новому маршруту?

person josiah    schedule 22.02.2015

Совместное использование Turbolinks и AngularJS

+1 @fiedl за отличный ответ. Но я предпочитаю использовать page:change вместе с page:load, потому что это обеспечивает некоторую гибкость: DOM может получать событие page:load из источников, отличных от турболинков, поэтому вам может не понадобиться такой же обратный вызов.

Наблюдение за page:change, затем page:load должно ограничить ваше поведение обратного вызова исключительно событиями, инициированными turbolinks.

function boostrapAngularJS () {
    angular.bootstrap(document.body, ['My Application']);
    addCallbackToPageChange();
}
function addCallbackToPageChange() {
    angular.element(document).one('page:change', function () {
        angular.element(this).one('page:load', boostrapAngularJS);
    });
}
addCallbackToPageChange();

(Это позволит/потребует от вас сохранить объявление ng-app в вашем html, как обычно при работе с AngularJS.)

person JellicleCat    schedule 27.06.2015

Turbolinks автоматически извлекает страницу, заменяет ее <body> и объединяет ее <head>, и все это без затрат на полную загрузку страницы.

https://github.com/turbolinks/turbolinks#turbolinks

Итак, вместо добавления директивы ng-app к элементу <html> мы можем просто сделать это для элемента <body>.

<html>
  <body ng-app=“yourApplicationModuleName">
  </body>
</html>
person Repolês    schedule 28.04.2016