31

分離スコープを必要とするディレクティブを作成していますが、 ngModelを介して親スコープにバインドしたいと考えています。

ここでの問題は、親のスコープ値が変更されていないことです。

マークアップ

<form name="myForm" ng-app="customControl">
    <div ng-init="form.userContent"></div>
    <div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>
    <span ng-show="myForm.myWidget.$error.required">Required!</span>
    <hr />
    <textarea ng-model="form.userContent"></textarea>
</form>

JS

angular.module('customControl', []).directive('contenteditable', function() {
    return {
        restrict : 'A', // only activate on element attribute
        require : '?ngModel', // get a hold of NgModelController
        scope: {},
        link : function(scope, element, attrs, ngModel) {
            if (!ngModel)
                return; // do nothing if no ng-model

            // Specify how UI should be updated
            ngModel.$render = function() {
                element.html(ngModel.$viewValue || '');
            };

            // Listen for change events to enable binding
            element.bind('blur keyup change', function() {
                        scope.$apply(read);
                    });
            read(); // initialize

            // Write data to the model
            function read() {
                ngModel.$setViewValue(element.html());
            }
        }
    };
});

デモ:フィドル.

ディレクティブに分離スコープを使用しない場合、これは正常に機能します

デモ:フィドル.

4

3 に答える 3

45

その理由は、contenteditableディレクティブの分離スコープを作成しているためng-model、同じ要素のディレクティブもその分離スコープを取得するためです。つまり、相互に接続されていない2つの異なるスコープがあり、どちらもform.userContent個別に変更されるプロパティを持っています。私はあなたがこのコードによってそれを例示することができると思います:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
    <script>
    angular.module('myApp', []).controller('Ctrl', function($scope) {

    })
    .directive('contenteditable', function() {
        return {
            restrict : 'A', // only activate on element attribute
            require : '?ngModel', // get a hold of NgModelController
            scope: {},
            link : function(scope, element, attrs, ngModel) {
                if (!ngModel)
                    return; // do nothing if no ng-model

                setInterval(function() {
                    if (angular.element('#contenteditable').scope().form)
                        console.log(angular.element('#contenteditable').scope().form.userContent);

                    if (angular.element('#textarea').scope().form)
                        console.log(angular.element('#textarea').scope().form.userContent);
                }, 1000);

                // Specify how UI should be updated
                ngModel.$render = function() {
                    element.html(ngModel.$viewValue || '');
                };

                // Listen for change events to enable binding
                element.bind('blur keyup change', function() {
                            scope.$apply(read);
                        });
                read(); // initialize

                // Write data to the model
                function read() {
                    ngModel.$setViewValue(element.html());
                }
            }
        };
    });
    </script>
</head>
<body ng-controller="Ctrl">
    <form name="myForm">
        <div ng-init="form.userContent"></div>
        <div contenteditable name="myWidget" ng-model="form.userContent" id="contenteditable" required>Change me!</div>
        <span ng-show="myForm.myWidget.$error.required">Required!</span>
        <hr />
        <textarea ng-model="form.userContent" id="textarea"></textarea>
    </form>
</body>
</html>

コンソールに表示されるように、2つの異なるスコープがありform.userContent、textareaのテキストを変更した場合、またはcontenteditable divのテキストを変更した場合、スコープは別々に変更されます。

ですから、あなたは「説明で十分であり、解決策を教えてください」と考えているに違いありません。まあ、これに対する(私の知る限り)きれいな解決策はありませんが、うまくいくものはあります。モデルの参照を分離スコープに取り込み、分離スコープで親スコープと同じ名前になっていることを確認します。

このような空のスコープを作成する代わりに、次のようにします。

...
scope: {}
...

次のようにモデルをバインドします。

...
scope: {
    model: '=ngModel'
}
....

これで、親スコープmodelの参照である分離スコープのプロパティができました。form.userContentしかしng-modelmodelプロパティを探しているのform.userPropertyではなく、分離されたスコープにまだ存在していないプロパティを探しています。したがって、これを修正するために、リンク関数内にこれを追加します。

scope.$watch('model', function() {
    scope.$eval(attrs.ngModel + ' = model');
});

scope.$watch(attrs.ngModel, function(val) {
    scope.model = val;
});

最初のウォッチform.userContentは、ディレクティブの外部からの変更を分離されたものに同期し、2番目のウォッチは、分離された変更を親スコープまでform.userContent確実に伝播します。form.userContent

これは長い答えであり、おそらく従うのは簡単ではないことを私は理解しています。ですから、ぼやけていると感じたものはすべて明確にしたいと思います。

于 2013-03-07T13:22:38.147 に答える
4

最初の答えは問題をよく説明しています。余分な時計を避ける簡単な解決策があると思います。

回答を要約するには 1. ngModel は、バインドする予定の要素がそのスコープ内にないため、isolate スコープ内では機能しません。それらは親スコープにあります。

解決策 1、親のプロパティにバインドする

<div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>

になる

<div contenteditable name="myWidget" ng-model="$parent.form.userContent" required>Change me!</div>

ソリューション 2、分離スコープの外に ngModel を移動する

require : '?ngModel',^ になりrequire : '?^ngModel',ます ngModel の親要素を調べるようにディレクティブに指示します

<div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>

になる

<div ng-model="form.userContent">
    <div contenteditable name="myWidget" required>Change me!</div>
</div>
于 2015-09-03T03:49:16.760 に答える