1

angularjsで「その場で編集」ディレクティブを書きたいです。そのディレクティブを再利用できるようにしたいので、ディレクティブには次の要件があります。

  1. 任意の要素をデコレートできる属性である必要があります。これは理にかなっています (div,span,li)
  2. 編集ボタンをサポートする必要があります。これをクリックすると、表示されている要素のセットが入力フィールドに変更されます。通常、連絡先 (番号、名前) などの 1 つのオブジェクトのプロパティ

このフィドルhttp://jsfiddle.net/honzajde/ZgNbU/1/で見られるディレクティブで、スコープの可視性のトリッキーな動作を発見しました。

  1. ディレクティブでコメントアウト: template と scope -> contact.number と contact.name が表示されます
  2. ディレクティブでコメントアウト: scope -> contact.number のみ表示
  3. 何もコメントアウトしていない → 何も表示されない

=> 両方がコメントアウトされている場合、テンプレートをディレクティブに追加するだけで、テンプレートが使用されていなくても contact.number がレンダリングされます。

ゲームのルールは何ですか?

<div>
  <div ng-controller="ContactsCtrl">
    <h2>Contacts</h2>
    <br />
    <ul>
        <li ng-repeat="contact in contacts">
            <span edit-in-place="" ng-bind="contact.number"></span> | 
            <span edit-in-place="" >{{contact.name}}</span>
        </li>
    </ul>
    <br />
    <p>Here we repeat the contacts to ensure bindings work:</p>
    <br />
    <ul>
        <li ng-repeat="contact in contacts">
            {{contact.number}} | {{contact.name}}
        </li>
    </ul>

  </div>
</div>


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

app.directive( 'editInPlace', function() {
  return {
    restrict: 'A',
    //scope: { contact:"=" },    
    template: '<span ng-click="edit()" ng-bind="value"></span><input ng-model="value"></input>',
    link: function ( $scope, element, attrs ) {
      // Let's get a reference to the input element, as we'll want to reference it.
      var inputElement = angular.element( element.children()[1] );

      // This directive should have a set class so we can style it.
      element.addClass( 'edit-in-place' );

      // Initially, we're not editing.
      $scope.editing = false;

      // ng-click handler to activate edit-in-place
      $scope.edit = function () {
        $scope.editing = true;

        // We control display through a class on the directive itself. See the CSS.
        element.addClass( 'active' );

        // And we must focus the element. 
        // `angular.element()` provides a chainable array, like jQuery so to access a native DOM function, 
        // we have to reference the first element in the array.
        inputElement[0].focus();
      };

      // When we leave the input, we're done editing.
      inputElement.prop( 'onblur', function() {
        $scope.editing = false;
        element.removeClass( 'active' );
      });
    }
  };
});

app.controller('ContactsCtrl', function ( $scope ) {
  $scope.contacts = [
    { number: '+25480989333', name: 'sharon'},
    { number: '+42079872232', name: 'steve'}
  ];
});
4

2 に答える 2

8

angularを誤用しているため、問題が発生しています。

まず、ディレクティブは自己完結型である必要がありますが、ディレクティブから機能を引き出しているため、汎用性が低く、再利用性が低くなります。コードでは、DOM とディレクティブに属するコントローラーに機能があります。なんで?

第 2 に、マークアップや JavaScript からは、これらすべてのピースをつなぎ合わせたときに達成したいことも明確ではありません。

3 番目に、ほとんどの場合、ディレクティブは独自の分離スコープを持つ必要があります。これは、バインドする属性を持つスコープ オブジェクトを宣言することによって行われます。{{contact.name}}ディレクティブ内で式 (つまり ) を渡さないでください。バインディングが壊れ、その場での編集が終了したときに連絡先が更新されないためです。=適切な方法は、スコープのプロパティを通じて双方向バインディングを確立することです。ng-bindここで必要なものではありません。これはスコープ固有であるため、ディレクティブのスコープ内で使用します。Valentynが示唆したように、これを回避するためにいくつかの魔法を使うことができますが、それは良い考えではなく、正しい方法で設定するのは非常に簡単です. 属性でこれを行うことの問題は何ですか?

これはすべて悪いジュジュです。

この同じトピックに関する他の質問で指摘したように、ディレクティブを自己完結型にして、それに対してではなく角度を付けて作業する必要があります。これは、以前に提供したフィドルの属性ベースのバージョンで、最初の要件を満たしています。この実装で具体的に何が問題なのか教えてください。それを修正する角度のある方法について話すことができます。

最後に、「ボタン」に関して必要なものについてさらにコンテキストを提供する場合は、それもフィドルに組み込みます。


[アップデート]

ディレクティブを自分のやり方で機能させることは可能ですが、最終的には問題が発生します (または現在、そう思われます)。Angular アプリ (またはそのことについては任意のアプリ) のすべてのコンポーネントは、可能な限り自己完結型である必要があります。これは「ルール」や制限ではありません。それは「ベストプラクティス」です。同様に、ディレクティブ コンポーネント間の通信はコントローラーを介して行うことができますが、そうすべきではありません。理想的には、コントローラで DOM をまったく参照すべきではありません。それがディレクティブの目的です。

特定の目的が編集可能な行である場合、それがディレクティブです。より大きなディレクティブが使用する下位レベルの一般的なその場編集ディレクティブを使用しても問題ありませんが、上位レベルのディレクティブも依然として存在します。上位レベルのディレクティブは、それらの間のロジックをカプセル化します。この上位レベルのコンポーネントには、連絡先オブジェクトが必要です。

ng-bind="var"最後に、いいえ、 と の間に必ずしも大きな違いがあるわけではありません{{var}}。しかし、それは問題ではありません。問題はそのバインディングが行われる場所でした。あなたの例では、双方向にバインドされたvariableの代わりにがディレクティブに渡されました。私のポイントは、ディレクティブが変数にアクセスして変更できるようにする必要があるということでした。

概要: 非常に jQuery スタイルの方法でコーディングしています。これは jQuery でのコーディングには最適ですが、Angular でのコーディングではうまく機能しません。実際、あなたが経験しているように、それは多くの問題を引き起こします。たとえば、jQuery では、DOM 要素を動的に挿入し、イベントを宣言して処理し、変数をすべて 1 つのコード ブロック内で手動でバインドします。Angular では、問題が明確に分離されており、バインディングのほとんどが自動的に行われます。ほとんどの場合、JavaScript コードは jQuery の代替案より少なくとも3 分の 2 小さくなります。これはそれらのケースの 1 つです。

そうは言っても、私は編集インプレースと追加機能を組み込むための新しい上位レベルのディレクティブの両方のより洗練されたバージョンを含むプランカーを作成しました: http://plnkr.co/edit/LVUIQD?p=プレビュー

これが役立つことを願っています。

【アップデート2】

これらは、あなたの新しい一連の質問に対する答えです。それらはあなたの教化には良いかもしれませんが、私はすでにあなたの問題を解決するための「角度のある方法」をあなたに与えました. また、元の回答と更新の両方で、これらの質問に(より広いストロークで)すでに対処していることがわかります。うまくいけば、これはそれをより明確にします。

質問:「ディレクティブでコメントアウト: template と scope -> contact.number と contact.name が表示される」

私の回答:スコープを指定しない場合、ディレクティブはその親スコープを継承します。親のコンテキスト内で名前と番号をバインドして補間したため、「機能」します。ただし、ディレクティブは値を変更するため、これは問題を解決する良い方法ではありません。それは本当にそれ自身の範囲を持つべきです。

質問:「ディレクティブでコメントアウト: scope -> contact.number のみが表示される」

私の返信:親のスコープ プロパティを "contact.number" ディレクティブにバインドしたため、ディレクティブが処理された後、$digest ループ中に内部に配置されます。「contact.name」では、ディレクティブに配置します。これは、ディレクティブがトランスクルージョンをコードしている場合にのみ機能します。

質問:「何もコメントアウトしていない -> 何も表示されない」

私の返答:そうですね。ディレクティブが独自のスコープを持っている場合 (これは間違いなくそうあるべきです)、定義済みのディレクティブ スコープ プロパティを使用して値を伝達する必要があります。scopeただし、定義でプロパティを使用して明示的に禁止している場合、コードはディレクティブで親スコープを使用しようとします。

要約:この 2 番目の更新は参考になるかもしれませんが (そうなることを願っています)、あなたの質問の下にある質問には答えていません。は?私の最初の投稿とその後の更新で、その質問に答えてください。

于 2013-01-05T17:52:38.910 に答える
2

これはフィドルを少し更新したものですが、要件の完全なリストを満たすにはさらに改善する必要があります: http://jsfiddle.net/5VRFE/

キーポイントは次のとおりです。

scope: { value:"=editInPlace" },

いくつかの注意事項: css クラスを変更する代わりに、表示と非表示を視覚化するために ng-show ng-hide ディレクティブを使用することをお勧めします。また、機能をさまざまなディレクティブに分散して、懸念事項をより適切に分離することをお勧めします (ngBlur ディレクティブを確認してください)。

スコープパラグラフに関するスコープチェックガイドの混乱について「トランスクルージョンとスコープの理解」: ディレクティブのテンプレートからコントローラーのスコープにアクセスしたい場合は、各ディレクティブには個別の分離スコープがあります。 . また、トランスクルージョン要素には、トランスクルージョン テンプレートを定義した場所からのスコープがあります。

最初のビューから、これらの分離されたスコープは少し奇妙に聞こえますが、適切に構造化されたディレクティブがある場合 (1 つのディレクティブが別のディレクティブを要求し、バインディングを共有できることに注意してください)、非常に便利であることがわかります。

于 2013-01-05T14:11:26.507 に答える