AngularJS-ng-modelを使用するディレクティブを作成する


294

ディレクティブを作成する要素と同じng-modelで入力フィールドを作成するディレクティブを作成しようとしています。

これが私がこれまでに思いついたものです:

HTML

<!doctype html>
<html ng-app="plunker" >
<head>
  <meta charset="utf-8">
  <title>AngularJS Plunker</title>
  <link rel="stylesheet" href="style.css">
  <script>document.write("<base href=\"" + document.location + "\" />");</script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
  <script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
  This scope value <input ng-model="name">
  <my-directive ng-model="name"></my-directive>
</body>
</html>

JavaScript

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

app.controller('MainCtrl', function($scope) {
  $scope.name = "Felipe";
});

app.directive('myDirective', function($compile) {
  return {
    restrict: 'E',
    scope: {
      ngModel: '='
    },
    template: '<div class="some"><label for="{{id}}">{{label}}</label>' +
      '<input id="{{id}}" ng-model="value"></div>',
    replace: true,
    require: 'ngModel',
    link: function($scope, elem, attr, ctrl) {
      $scope.label = attr.ngModel;
      $scope.id = attr.ngModel;
      console.debug(attr.ngModel);
      console.debug($scope.$parent.$eval(attr.ngModel));
      var textField = $('input', elem).
        attr('ng-model', attr.ngModel).
        val($scope.$parent.$eval(attr.ngModel));

      $compile(textField)($scope.$parent);
    }
  };
});

ただし、これがこのシナリオを処理する正しい方法であるとは確信しておらず、ng-modelターゲットフィールドの値でコントロールが初期化されないというバグがあります。

:ここでは上記のコードのPlunkerだhttp://plnkr.co/edit/IvrDbJは、

これを処理する正しい方法は何ですか?

編集ng-model="value"テンプレートからを削除した後、これは正常に動作しているようです。ただし、これが正しい方法であることを再確認したいので、この質問は開いたままにしておきます。


1
削除scopeして設定するとどうなりますscope: falseか?ng-modelその場合のバインド方法は?
Saeed Neamati

回答:


210

編集:この回答は古く、おそらく古くなっています。それは人々を迷わせないように、ただの頭を上げるだけです。私はもうAngularを使用していないので、改善するための良い立場にはありません。


実際にはかなり良いロジックですが、少し簡単にすることができます。

指令

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

app.controller('MainCtrl', function($scope) {
  $scope.model = { name: 'World' };
  $scope.name = "Felipe";
});

app.directive('myDirective', function($compile) {
  return {
    restrict: 'AE', //attribute or element
    scope: {
      myDirectiveVar: '=',
     //bindAttr: '='
    },
    template: '<div class="some">' +
      '<input ng-model="myDirectiveVar"></div>',
    replace: true,
    //require: 'ngModel',
    link: function($scope, elem, attr, ctrl) {
      console.debug($scope);
      //var textField = $('input', elem).attr('ng-model', 'myDirectiveVar');
      // $compile(textField)($scope.$parent);
    }
  };
});

ディレクティブ付きのHTML

<body ng-controller="MainCtrl">
  This scope value <input ng-model="name">
  <my-directive my-directive-var="name"></my-directive>
</body>

CSS

.some {
  border: 1px solid #cacaca;
  padding: 10px;
}

このPlunkerで実際に動作する様子を見ることができます。

これが私が見るものです:

  • 「ng-model」を使用する理由を理解していますが、あなたの場合は必要ありません。ng-modelは、既存の html要素をスコープ内の値にリンクすることです。ディレクティブを自分で作成しているため、「新しい」html要素を作成しているため、ng-modelは必要ありません。

編集マークのコメントで述べたように、慣習を守るためだけにng-modelを使用できない理由はありません

  • ディレクティブでスコープを明示的に作成することにより(「分離された」スコープ)、ディレクティブのスコープは親スコープの「name」変数にアクセスできません(そのため、ng-modelを使用したいと思います)。
  • ngModelをディレクティブから削除し、任意の名前に変更できるカスタム名に置き換えました。
  • すべてが機能するのは、スコープ内の「=」記号です。ドキュメントのチェックアウト ドキュメントを「スコープ」ヘッダの下。

一般的に、ディレクティブの値を常に親スコープの値にマップする場合は、ディレクティブは分離スコープ(正しく実行したもの)を使用し、「=」タイプのスコープを使用する必要があります。


18
+1ですが、「ng-modelは既存のHTML要素をスコープ内の値にリンクすることです」という記述に同意するかどうかはわかりません。contenteditableAngularドキュメントの2つのディレクティブの例(フォームページNgModelControllerページ)では、どちらもng-modelを使用しています。そして、ngModelControllerページは、このコントローラーが「他のディレクティブによって拡張されることを意味する」と述べています。
Mark Rajcok、2013

33
この回答が元の質問の目的を達成しないため、なぜこの回答がそれほど高く評価されているのかはわかりません。ngModelを使用することです。はい、親コントローラーに状態を置くことでngModelの使用を回避できますが、これは2つのコントローラーが強くバインドされ、それらを個別に使用/再利用できないという犠牲を伴います。2つのコンポーネント間でリスナーを設定する代わりに、グローバル変数を使用するようなものです。技術的には簡単かもしれませんが、ほとんどの場合、これは良い解決策ではありません。
Pat Niemeyer 2014

親コントローラーに依存したい場合は、とにかく 'require:^ parent'を挿入する必要があることを追加します-必要に応じて依存関係を明示的およびオプションにすることができるようにします。
Pat Niemeyer 2014

3
@Jeroen主な利点は、モデルが渡される他の場所との整合性ですhg-model(結合の問題ではなく、IMO)。このように、データコンテキストは、<input>それがカスタムディレクティブであるかカスタムディレクティブであるかにかかわらず、常にng-modelを使用するため、HTMLライターの認識オーバーヘッドが簡素化されます。つまりmy-directive-var、特にオートコンプリートがないため、HTMLライターが各ディレクティブの名前を調べる必要がなくなります。
zai chang 2015

2
うーん... ok ...しかし、これは今やng-model-options他のngモデルのものでは機能しません、そうですか?
George Mauer、2016

68

私はすべての答えの組み合わせを採用しましたが、ng-model属性でこれを行うには2つの方法があります。

  • ngModelをコピーする新しいスコープを使用
  • リンクでコンパイルする同じスコープで

リンク時のコンパイルが好きかどうかはわかりません。ただし、要素を別のものに置き換えるだけの場合は、その必要はありません。

全体として、私は最初のものを好みます。スコープを{ngModel:"="}設定ng-model="ngModel"し、テンプレートのどこに配置するかを設定するだけです。

更新:コードスニペットをインライン化し、Angular v1.2用に更新しました。特にjQueryを使用しない場合は、スコープの分離が依然として最善であることがわかります。つまり、次のようになります。

  • 単一の要素を置き換えますか?それを置き換えるだけで、スコープはそのままにしますが、v2.0ではreplaceは非推奨です。

    app.directive('myReplacedDirective', function($compile) {
      return {
        restrict: 'E',
        template: '<input class="some">',
        replace: true
      };
    });
    
  • それ以外の場合は、これを使用します。

    app.directive('myDirectiveWithScope', function() {
      return {
        restrict: 'E',
        scope: {
          ngModel: '=',
        },
        template: '<div class="some"><input ng-model="ngModel"></div>'
      };
    });
    

1
3つのスコープのすべての可能性と、テンプレートの子要素またはテンプレートのルート要素のプランカーを更新しました。
2013年

1
これはすばらしいことですが、基本的にどのようにこれをオプションにしていますか?UIライブラリのテキストボックスディレクティブを作成しています。モデルをオプションにしたいので、ngModelが設定されていなくてもテキストボックスは機能します。
Nick Radford

1
@NickRadford ngModelが$ scopeで定義されているかどうかを確認し、定義されていない場合は使用しないでください。
2013

1
ng-model分離されたスコープでの再利用に問題や追加のオーバーヘッドはありますか?
Jeff Ling

2
@jefflingわかりませんが、そうは思いません。ngModelのコピーはかなり軽量で、孤立したスコープは露出を制限します。
2014

52

それはそれほど複雑ではありません:あなたの方言では、エイリアスを使用してください: scope:{alias:'=ngModel'}

.directive('dateselect', function () {
return {
    restrict: 'E',
    transclude: true,
    scope:{
        bindModel:'=ngModel'
    },
    template:'<input ng-model="bindModel"/>'
}

あなたのhtmlでは、通常どおりに使用してください

<dateselect ng-model="birthday"></dateselect>

1
これは、Kendo UIのようなライブラリーを扱う場合に、はるかに簡単です。ありがとう!
バイトベンダー、

30

モデルの$ viewValueまたは$ modelValueにアクセスする必要がある場合にのみ、ng-modelが必要です。NgModelControllerを参照してください。その場合は、を使用しますrequire: '^ngModel'

残りについては、ロイスの回答を参照してください。


2
ng-modelは、$ viewValueや$ modelValueが必要ない場合にも役立ちます。@kolrieの例のように、ng-modelのデータバインディング機能のみが必要な場合でも役立ちます。
Mark Rajcok 2013

1
そして、^ng-modelが親要素に適用されている場合にのみ存在するはずです
georgiosd

18

これは、少し遅れての答えですが、私はこの素晴らしいた記事についてNgModelController、私はあなたが探していたまさにだと思います、。

TL; DR-使用して、リンク機能にrequire: 'ngModel'追加できNgModelControllerます。

link: function(scope, iElement, iAttrs, ngModelCtrl) {
  //TODO
}

このように、ハックは必要ありません-あなたはAngularの組み込みを使用しています ng-model


2

私は属性を介してngmodelを設定しません。テンプレートで直接指定できます:

template: '<div class="some"><label>{{label}}</label><input data-ng-model="ngModel"></div>',

plunkerhttp : //plnkr.co/edit/9vtmnw?p=preview


0

Angular 1.5以降、コンポーネントを使用することが可能です。コンポーネントは持ち運びが簡単で、この問題を簡単に解決します。

<myComponent data-ng-model="$ctrl.result"></myComponent>

app.component("myComponent", {
    templateUrl: "yourTemplate.html",
    controller: YourController,
    bindings: {
        ngModel: "="
    }
});

YourController内で行う必要があるのは次のとおりです。

this.ngModel = "x"; //$scope.$apply("$ctrl.ngModel"); if needed

私が見つけたのは、実際には「<」ではなく「=」を使用した場合に機能することです。この回答の「Inside YourController」の部分が何を意味するのかわかりません。これのポイントは、コンポーネント内にngModelを設定しないことですか?
マルク・ストーバー

1
@MarcStober「Inyour YourController」を使用して、ngModelがゲッターおよびセッターとして利用できることを示したかっただけです。この例では、$ ctrl.resultは "x"になります。
Niels Steenbeek、2017

OK。他の重要な部分は、コントローラーテンプレートでも実行できることinput ng-model="$ctrl.ngModel"で、$ ctrl.resultとも同期します。
Marc Stober 2017

0

分離スコープを作成することは望ましくありません。scope属性の使用は避け、次のようにします。scope:trueは新しい子スコープを提供しますが、分離はしません。次に、parseを使用して、ローカルスコープ変数を、ユーザーがngModel属性に指定した同じオブジェクトにポイントします。

app.directive('myDir', ['$parse', function ($parse) {
    return {
        restrict: 'EA',
        scope: true,
        link: function (scope, elem, attrs) {
            if(!attrs.ngModel) {return;}
            var model = $parse(attrs.ngModel);
            scope.model = model(scope);
        }
    };
}]);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.