AngularJS: $viewContentLoaded, выпущенный до частичного просмотра
Для частичного представления я хочу сделать некоторые материалы JavaScript, которые я обычно делал бы с $(document).ready(function() {...})
, например. свяжите венец-слушателей с элементами. Я знаю, что это не работает для AngularJS и частичных представлений, загруженных в представление "root".
Таким образом, я добавил слушателя к контроллеру, который прослушивает событие $viewContentLoaded
. Вызывается функция прослушивателя, поэтому событие запускается, но мне кажется, что это происходит до того, как будет показано частичное представление. Я также не вижу элементы, когда я устанавливаю точку останова в функции слушателя и отлаживаю ее с помощью firebug, а также выбор jquery внутри функции не находит частичный элемент представления.
Вот что выглядит контроллер:
angular.module('docinvoiceClientAngularjsApp')
.controller('LoginController', function ($scope, $rootScope) {
$scope.$on('$viewContentLoaded', function(event) {
console.log("content loaded");
console.log($("#loginForm")); // breakpoint here
});
[...]
Я предполагаю, что я делаю что-то не так, поскольку в stackoverflow должно быть больше сообщений, если это обычная ошибка.
Поскольку я использую ui-router и ui-view, я дам вам отрывок файла маршрутизации:
angular
.module('docinvoiceClientAngularjsApp', [
'ui.router',
'ngAnimate',
'ngCookies',
'ngResource',
'ngMessages',
'ngRoute',
'ngSanitize',
'ngTouch'
])
.config(function ($routeProvider, $stateProvider) {
$stateProvider
.state('login', {
url: '/',
templateUrl: 'components/login/loginView.html',
controller: 'LoginController'
})
.run(['$state', function ($state) {
$state.transitionTo('login');
}])
[...]
Любая помощь приветствуется. Спасибо и любезны.
ОБНОВЛЕНИЕ 1: Я удалил ошибку до следующей версии usecase: loginView.html выглядит следующим образом:
<div id="loginContainer" style="width: 300px">
<form id="loginForm" ng-submit="login(credentials)" ng-if="session.token == undefined">
[...]
Как только я удаляю ng-if
из тега div, он работает так, как ожидалось. Событие запускается после рендеринга DOM, поэтому jQuery находит элемент. Если ng-if
прикреплен к тегу div, поведение будет описано вначале.
ОБНОВЛЕНИЕ 2: Как и было обещано, я добавил рабочую демонстрацию, в которой показано различное поведение при добавлении директивы ng-if
. Может ли кто-нибудь указать мне правильное направление? Не придерживайтесь формы входа в систему как таковой, так как есть много других вариантов использования, когда я хочу удалить определенные части представления на основе некоторого выражения и сделать некоторые файлы JavaScript после частичного просмотра.
Здесь вы можете найти рабочую демонстрацию: Демо
Ответы
Ответ 1
Это связано с циклом digest angular, это о том, как angular работает под капотом, привязыванием данных и т.д. Есть большие учебники, объясняющие это.
Чтобы решить вашу проблему, используйте $timeout, это сделает код выполненным в следующем цикле, если ng-if уже был разобран:
app.controller('LoginController', function ($scope, $timeout) {
$scope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
$scope.formData.value = document.getElementById("loginForm").id;
},0);
});
});
Исправлено демо: http://codepen.io/anon/pen/JoYPdv
Но я настоятельно рекомендую вам использовать директивы для любых манипуляций с DOM, контроллер не для этого.
Вот пример того, как это сделать:
Простое манипулирование домом в AngularJS - нажмите кнопку, затем установите фокус на элемент ввода
Ответ 2
Я решил эту проблему с помощью директив. Добавьте один директиву в свой элемент (например, <div my-dir></div>
) и выполните манипуляции с элементом в соответствующей директиве следующим образом:
app.directive('myDir', function () {
return {
restrict: 'A',
link: function (scope, element) {
// Do manipulations here with the help of element parameter
}
};
});
Я также пробовал события с государственным провайдером, такие как $stateChangeSuccess
, $viewContentLoaded
, но не смог решить проблему. Потому что после того, как эти события были уволены, требуется время для рендеринга в DOM.
Итак, мы можем следовать этому подходу, который дает отличные результаты и правильный способ реализации в Angular JS:)
Ответ 3
Это ответ Аиду немного позже, но может помочь кому-то другому. Я настраиваю службу, которую позже я смогу позвонить с контроллера или директивы.
'use strict';
app.factory('viewContentLoaded', function($q, $rootScope, $timeout) {
var viewContentLoaded = $q.defer(),
foo = function() {
$timeout(function() {
viewContentLoaded.resolve();
// Get all entries
}, 100);//Timeout
},
checkViewContenLoadedListner = $rootScope.$on('$viewContentLoaded', foo);//Rootscope
return {
getLoaded: function() {
return viewContentLoaded.promise;
},
removeViewContenListner: function() {
//Remove event listern on $viewContentLoaded no $off so call it will unsubscribe it
//$rootScope.$off('$viewContentLoaded', foo);//Rootscope
//Don't forget to unsubscribe later
checkViewContenLoadedListner();
}
};
});