18

anugularJS を使用して n レベルの階層的な順序なしリストを生成しようとしていますが、正常に生成できました。しかし今、ディレクティブとコントローラーの間でスコープの問題が発生しています。ディレクティブ テンプレートで ng-click を介して呼び出される関数内から、親のスコープ プロパティを変更する必要があります。

http://jsfiddle.net/ahonaker/ADukg/2046/を参照してください-これがJSです

var app = angular.module('myApp', []);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.itemselected = "None";
    $scope.organizations = {
        "_id": "SEC Power Generation",
        "Entity": "OPUNITS",
        "EntityIDAttribute": "OPUNIT_SEQ_ID",
        "EntityID": 2,
        "descendants": ["Eastern Conf Business Unit", "Western Conf Business Unit", "Atlanta", "Sewanee"],
        children: [{
            "_id": "Eastern Conf Business Unit",
            "Entity": "",
            "EntityIDAttribute": "",
            "EntityID": null,
            "parent": "SEC Power Generation",
            "descendants": ["Lexington", "Columbia", "Knoxville", "Nashville"],
            children: [{
                "_id": "Lexington",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 10,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Columbia",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 12,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Knoxville",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 14,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Nashville",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 4,
                "parent": "Eastern Conf Business Unit"
            }]
        }]
    };

    $scope.itemSelect = function (ID) {
        $scope.itemselected = ID;
    }
}

app.directive('navtree', function () {
    return {
        template: '<ul><navtree-node ng-repeat="item in items" item="item" itemselected="itemselected"></navtree-node></ul>',
        restrict: 'E',
        replace: true,
        scope: {
            items: '='
        }
    };
});

app.directive('navtreeNode', function ($compile) {
    return {
        restrict: 'E',
        template: '<li><a ng-click="itemSelect(item._id)">{{item._id}} - {{itemselected}}</a></li>',
        scope: {
            item: "=",
            itemselected: '='
        },
        controller: 'MyCtrl',
        link: function (scope, elm, attrs) {
            if ((angular.isDefined(scope.item.children)) && (scope.item.children.length > 0)) {
                var children = $compile('<navtree items="item.children"></navtree>')(scope);
                elm.append(children);
            }
        }
    };
});

ここにHTMLがあります

<div ng-controller="MyCtrl">
    Selected: {{itemselected}}

    <navtree items="organizations.children"></navtree>
</div>

リストはモデルから生成されることに注意してください。そして ng-click は関数を呼び出して親スコープ プロパティ (itemselected) を設定しますが、変更はローカルでのみ発生します。アイテムをクリックしたときに予想される動作は、「選択済み: なし」が「選択済み: xxx」に変わることです。ここで、xxx はクリックされたアイテムです。

親スコープとディレクティブの間でプロパティを適切にバインドしていませんか? プロパティの変更を親スコープに渡すにはどうすればよいですか?

これが明確であることを願っています。

助けてくれてありがとう。

4

3 に答える 3

18

この動作中のフィドルをご覧くださいhttp://jsfiddle.net/eeuSv/

私がしたことは、navtree-nodeディレクティブ内で親コントローラーを要求し、そのコントローラーで定義されたメンバー関数を呼び出すことでした。メンバー関数はsetSelected. this.setSelectedではないことに注意してください$scope.setSelected。次に、navtree-nodeスコープ メソッドを定義しますitemSelect。アンカー タグをクリックすると、スコープでitemSelectメソッドが呼び出されます。navtree-nodeこの inturn はsetSelected、選択した ID を渡してコントローラーのメンバー メソッドを呼び出します。

scope.itemSelect = function(id){ myGreatParentControler.setSelected(id) }

于 2013-03-13T19:00:41.033 に答える
11

Maxdec は正しく、これはスコーピングに関係しています。残念ながら、これは AngularJS のドキュメントが (私のような) 初心者にとって誤解を招く可能性があるほど複雑なケースです。

警告:私がこれを説明しようとすると、少し長文になることを覚悟しておいてください。コードだけを見たい場合は、このJSFiddleにアクセスしてください。また、AngularJS について学習するには、 egghead.ioのビデオが非常に貴重であることもわかりました。

問題についての私の理解は次のとおりです。ディレクティブの階層(navtree、navitem)があり、「ツリーを上る」navitemからルートコントローラーに情報を渡したいとします。AngularJS は、一般的に適切に記述された Javascript と同様に、変数のスコープを厳密に設定するように設定されているため、ページで実行されている他のスクリプトを誤って台無しにすることはありません。

&Angular には、isolate スコープを作成し、親スコープで関数を呼び出すことができる特別な構文 ( ) があります。

// in your directive
scope: {
   parentFunc: '&'
}

ここまでは順調ですね。複数レベルのディレクティブがある場合、基本的に次のことを行う必要があるため、注意が必要です。

  1. 変数を受け入れてモデルを更新する関数をルート コントローラーに用意する
  2. 中間レベルのディレクティブ
  3. ルートコントローラーと通信できる子レベルのディレクティブ

問題は、子レベルのディレクティブがルート コントローラーを認識できないことです。私の理解では、次のように機能するディレクティブ構造に「チェーン」を設定する必要があります。

最初:ルート コントローラーに関数を返す関数を用意します (ルート ビュー コントローラーのスコープへの参照があります)。

$scope.selectFunctionRoot = function () {
    return function (ID) {
        $scope.itemselected = ID;
    }
}

2 番目:中間レベルのディレクティブをセットアップして、次のようなものを返す独自の選択関数 (子に渡す) を設定します。このコードが実際に実行されると、子レベルのディレクティブのコンテキストになるため、中間レベルのディレクティブのスコープをどのように保存する必要があるかに注意してください。

// in the link function of the mid-level directive. the 'navtreelist'
scope.selectFunctionMid = function () {
    // if we don't capture our mid-level scope, then when we call the function in the navtreeNode it won't be able to find the mid-level-scope's functions            
    _scope = scope;
    return function (item_id) {
        console.log('mid');
        console.log(item_id);

        // this will be the "root" select function
        parentSelectFunction = _scope.selectFunction();
        parentSelectFunction(item_id);
    };
};

3 番目:子レベルのディレクティブ ( navtreeNode) で、ローカル関数を呼び出す関数をバインドしng-clickます。ローカル関数は、ルート コントローラーまでずっと「チェーンを呼び出します」:

// in 'navtreeNode' link function
scope.childSelect = function (item_id) {
    console.log('child');
    console.log(item_id);

    // this will be the "mid" select function  
    parentSelectFunction = scope.selectFunction();
    parentSelectFunction(item_id);
};

これは、コードにコメントがあるJSFiddleの更新されたフォークです。

于 2013-03-13T18:45:29.413 に答える
3

これは、各ディレクティブが独自のスコープを作成するためである可能性があります (実際には、そうするように指示します)。
ディレクティブの詳細については、ここ、特に「ディレクティブの記述 (ロング バージョン)」の章を参照してください。

スコープ- に設定されている場合:

true - このディレクティブの新しいスコープが作成されます。同じ要素の複数のディレクティブが新しい​​スコープを要求する場合、新しいスコープは 1 つだけ作成されます。テンプレートのルートは常に新しいスコープを取得するため、新しいスコープ ルールはテンプレートのルートには適用されません。

{} (オブジェクト ハッシュ) - 新しい「分離」スコープが作成されます。'isolate' スコープは、親スコープから原型的に継承しないという点で、通常のスコープとは異なります。これは、親スコープのデータを誤って読み取ったり変更したりしてはならない、再利用可能なコンポーネントを作成する場合に役立ちます。

したがって、各ディレクティブには独自の「分離された」スコープがあるため、行った変更は MyCtrl スコープには反映されません。

そのため、クリックするとローカル$scope.itemselected変数のみが変更され、それらの「すべて」は変更されません。

于 2013-03-13T17:24:47.513 に答える