Я пытаюсь использовать фреймворк Angularjs в своем приложении с турболинками. После изменения страницы он не инициализирует новые прослушиватели событий. Есть ли способ заставить его работать? Заранее спасибо!
Использование angularjs с турболинками
Ответы (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>
...
Дальнейшее чтение
- Начальная загрузка AngularJS: http://docs.angularjs.org/guide/bootstrap
- Railscasts на Turbolinks (объясняет обратные вызовы): http://railscasts.com/episodes/390-turbolinks
- Демонстрационное приложение: https://github.com/fiedl/rails-5-angular-and-turbolinks-demo
ng-click="save()"
, а затем перезагружает представление... мой метод save() теперь срабатывает три раза. Есть идеи?
- person BradGreens; 18.07.2013
<head>
нашего администратора (таким образом, предотвратив несколько экземпляров моих скриптов). Это не публичный опыт, поэтому меня не слишком беспокоят подводные камни загрузки JS в разделе заголовка html.
- person BradGreens; 18.07.2013
Uncaught Error: [ng:btstrpd] App Already Bootstrapped with this Element 'document'
. По-видимому, элемент может быть загружен только один раз. Изменение angular.bootstrap(document, ['YourApplication'])
на angular.bootstrap("body", ['YourApplication'])
работает без проблем.
- person nates; 31.10.2013
$(document).on
и добавить вместо нее $ ->
- person Chris Edwards; 18.03.2014
unless $('body').hasClass('ng-scope')
(coffeescript!) После angular.bootstrap
- person rweng; 08.04.2014
$(document).on "turbolinks:load", -> …
. Спасибо!
- person Repolês; 11.07.2016
$(document).on('turbolinks:load', function() { angular.bootstrap(document.body, ['myApp']); })
Спасибо!
- person Ryan; 13.08.2016
Турболинки пытаются оптимизировать отрисовку страниц и конфликтуют с обычной загрузкой AngularJS.
Если вы используете Turbolinks в некоторых местах вашего приложения, а некоторые части используют Angular. Я предлагаю это элегантное решение:
Каждая ссылка на страницу angularapp (где вы используете ng-app="appname") должна иметь этот атрибут:
<a href="/myapp" data-no-turbolink>Say NO to Turbolinks</a>.
Второй, упомянутый в Stackoverflow, явно перезагружает/загружает каждое ng-приложение, обрабатывая событие page:load. Я бы хотел, чтобы это было навязчиво, не говоря уже о том, что вы потенциально загружаете что-то, чего нет на странице, поэтому тратите ресурсы впустую.
Я лично использовал вышеуказанное решение.
Надеюсь, поможет
В случае ошибки
Неперехваченная ошибка: [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'])
, но это вызывает вспышку нескомпилированного контента.
Turbolinks не совсем подходит для клиентской среды MVC. Turbolinks используется для удаления из ответа сервера всего, кроме тела. С MVC на стороне клиента вы должны просто передавать JSON клиенту, а не HTML.
В любом случае, turbolinks создает свои собственные обратные вызовы.
page:load
page:fetch
page:restore
page:change
Плагин jquery.turbolinks может инициировать загрузку модулей с помощью директив ng-app. Если вы пытаетесь вручную загрузить свои модули, jquery.turbolinks может привести к ошибкам ng:btstrpd. Одно предостережение, которое я обнаружил, заключается в том, что jquery.turbolinks зависит от события page:load
, которое может срабатывать до того, как любые новые теги <script>
для конкретной страницы завершат работу. Это может привести к $injector:nomod
ошибкам, если вы включите определения модулей вне application.js. Если вы действительно хотите, чтобы ваши модули были определены в отдельных файлах javascript, которые включены только на определенные страницы, вы можете просто отключить турбоссылки для любых ссылок на эти страницы через data-no-turbolink
.
Добавьте в приложение следующий обработчик событий.
Кофейный скрипт:
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
$(document).on('ready', bootstrapAngular)
.
- person Robin Daugherty; 11.02.2014
Основываясь на комментариях, которые я видел, единственный допустимый сценарий использования обоих вместе таким образом, что Angular будет конфликтовать с Turbolinks (например, когда я разрешаю Angular обрабатывать часть маршрутизации), — это если у меня есть существующее приложение, которое я m пытаюсь портировать на Angular.
Лично, если бы я делал это с нуля, я думаю, что лучшим решением было бы решить, что должно обрабатывать маршрутизацию, и придерживаться этого. Если Angular, то избавьтесь от Turbolinks -> это мало что даст вам, если у вас есть что-то близкое к одностраничному приложению. Если вы позволяете Rails обрабатывать маршрутизацию, то просто используйте Angular для организации поведения на стороне клиента, которое не может быть обработано сервером при обслуживании шаблонов.
Я пропустил сценарий, здесь? Мне не кажется элегантным пытаться разделить обязанности по маршрутизации между разными фреймворками, даже в большом приложении... Есть ли какой-нибудь другой сценарий, в котором Turbolinks будет мешать Angular, кроме обновления страницы или перехода к новому маршруту?
Совместное использование 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.)
Turbolinks автоматически извлекает страницу, заменяет ее
<body>
и объединяет ее<head>
, и все это без затрат на полную загрузку страницы.
https://github.com/turbolinks/turbolinks#turbolinks
Итак, вместо добавления директивы ng-app
к элементу <html>
мы можем просто сделать это для элемента <body>
.
<html>
<body ng-app=“yourApplicationModuleName">
</body>
</html>