2013-03-07 13 views
28

私は隔離されたスコープが必要なディレクティブを作成していますが、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()); 
      } 
     } 
    }; 
}); 

デモ:Fiddle

私はディレクティブ

デモのために分離されたスコープを使用していない場合、これが正常に動作します:Fiddle

+0

は 'element.html(ngModel $ viewValueが...。)' element.html($ sce.getTrustedHtml(ngModel $べきではありません。 viewValue)..) 'これはng docsの例とほぼ同じですが、これはそうしないとxss保護を効果的にバイパスしていることがわかりました。 – cirrus

+0

@arun隔離スコープが機能しない理由を説明できますか? – geckob

+0

Re @cirrusのコメントでは、XSSを避ける​​ために、何かが要素に配置されたときは、まず$ sanitize(つまりHTMLを信頼しない)で消さなければならないと思います。 element.html($ sanitize(ngModel。$ viewValue)) 'のようなものです。 – Soferio

答えて

41

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> 

、二つの異なるスコープとform.userContentがそれらにあるあなたがテキストエリアにしたり、変更する場合は、テキストを変更する場合は、別途変更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に私たちのディレクティブの外から来るform.userContentで変更を同期、および第二の時計は、我々はすべての変更を伝播することを確認します私たちの隔離されたform.userContent親スコープまで。

私はこれが長い答えであり、おそらくそれほど簡単ではないことを認識しています。だから私はあなたがぼやけていると感じるものはすべて晴らしています。

+2

"[...]同じ要素のng-modelディレクティブがその分離されたスコープを取得すると、つまり、互いに分離された2つのスコープがあることを意味します。 'ng-model'と' contenteditable'の両方で共有されていますが、どのように2つの異なるスコープがあると言えますか?それは矛盾のように見えます。または私は何かを逃していますか? – Behrang

+0

@anders私は分離スコープがどのようにコードを壊すのか明確ではない。あなたは明確にしていただけますか? – geckob

+0

@ Anders Ekdahl要素の複数のディレクティブが独立したスコープを提供する場合、新しいスコープは1つだけ適用されます(参照Ng-Book、111ページ、Scope Option見出し) –

4

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

要約回答1。分離しようとしている要素がその範囲にないため、ngModelは分離スコープ内では機能しません。それらは親スコープにあります。

ソリューション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となり、

require : '?ngModel',require : '?^ngModel', ^見て、あなたのディレクティブを告げるとなっ分離株範囲外ngModelを移動しますngModelの親要素内

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

はuが正確に欲しいものを知ってIdo't

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

この回答は、正当な(そして広く適用されている)解決法であるので、二重$時計のアプローチで答えを完成させたいかもしれません。 –

-1

、uはこれを試すことができなりました。

HTML:

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

JS

angular.module('customControl', []).directive('contenteditable', function() { 
    return { 
     restrict : 'A', // only activate on element attribute 
     require : '?ngModel', // get a hold of NgModelController 
     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({'content': element.html()}); 
      } 
     } 
    }; 
}); 
関連する問題