web-dev-qa-db-ja.com

AngularJS:部分ビューが表示される前に$ viewContentLoadedが発生しました

部分的なビューのために、私は通常$(document).ready(function() {...})で行うJavaScriptのようなことをしたいと思います。 venetリスナーを要素にバインドします。これは、AngularJSおよび「ルート」ビューにロードされた部分ビューでは機能しないことを知っています。

したがって、$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-routerui-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');
    }])

 [...]

どんな助けも大歓迎です。よろしくお願いします

UPDATE 1:エラーを次のユースケースに落としました: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タグに添付されている場合、動作は最初に説明したとおりです。

UPDATE 2:約束どおり、ng-ifディレクティブを追加するときの異なる動作を示す動作デモを追加しました。誰かが私に正しい方向を向けることができますか?いくつかの式に基づいてビューの特定の部分を削除し、部分的なビューの準備ができた後にJavaScriptの処理を行いたいユースケースが他にもたくさんあるため、ログインフォームに固執しないでください。

動作デモは次の場所にあります: Demo

25
Florian

これは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での簡単なdom操作-ボタンをクリックして、入力要素にフォーカスを設定します

44
T4deu

私はディレクティブの助けを借りてこの問題を解決しました。 1つのディレクトリを要素に追加し(<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 :)で実装する適切な方法を提供するこのアプローチに従うことができます。

これは少し遅れてHadesへの答えですが、他の誰かを助けるかもしれません。コントローラーまたはディレクティブから後で呼び出すことができるサービスをセットアップします。

'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();
        }

    };
});

コントローラーまたはディレクティブにviewContentLoadedを含め、promiseでget関数を呼び出します。アプリをアプリ名に変更し、ロードするサービスファイルを含めることを忘れないでください。

viewContentLoaded.getLoaded().then(function(){
    //Remove Listner when done
    viewContentLoaded.removeViewContenListner();
    //Your code to run       

}, function(reason) {
    //$log.error(reason);
});
0
Johan Wergelius