カスタムディレクティブ内で評価された属性を取得する方法


363

カスタムディレクティブから評価された属性を取得しようとしていますが、正しい方法を見つけることができません。

詳しく説明するために、このjsFiddleを作成しました。

<div ng-controller="MyCtrl">
    <input my-directive value="123">
    <input my-directive value="{{1+1}}">
</div>

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+attr.value);
    }
});

何が欠けていますか?


ディレクティブについて理解を深めるには、以下のリンクをたどってください。undefinednull.com/2014/02/11/...
プラザンナSasne

回答:


573

注意:より良い解決策が見つかれば、この回答を更新します。また、古い回答が関連している限り、将来参照できるように保持します。最新かつ最良の答えが最初です。

より良い答え:

angularjsのディレクティブは非常に強力ですが、それらの背後にあるプロセスを理解するには時間がかかります。

ディレクティブの作成中に、angularjsを使用すると、親スコープへのいくつかのバインディングを持つ分離スコープを作成できます。これらのバインディングは、DOMで要素をアタッチする属性と、ディレクティブ定義オブジェクトスコーププロパティを定義する方法によって指定されます

スコープで定義できるバインディングオプションには3つのタイプがあり、それらを接頭辞関連属性として記述します。

angular.module("myApp", []).directive("myDirective", function () {
    return {
        restrict: "A",
        scope: {
            text: "@myText",
            twoWayBind: "=myTwoWayBind",
            oneWayBind: "&myOneWayBind"
        }
    };
}).controller("myController", function ($scope) {
    $scope.foo = {name: "Umur"};
    $scope.bar = "qwe";
});

HTML

<div ng-controller="myController">
    <div my-directive my-text="hello {{ bar }}" my-two-way-bind="foo" my-one-way-bind="bar">
    </div>
</div>

その場合、ディレクティブのスコープ内で(リンク関数またはコントローラーにあるかどうかにかかわらず)、次のようにこれらのプロパティにアクセスできます。

/* Directive scope */

in: $scope.text
out: "hello qwe"
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings

in: $scope.twoWayBind
out: {name:"Umur"}
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope

// in directive's scope
in: $scope.twoWayBind.name = "John"

//in parent scope
in: $scope.foo.name
out: "John"


in: $scope.oneWayBind() // notice the function call, this binding is read only
out: "qwe"
// any changes here will not reflect in parent, as this only a getter .

「それでも大丈夫」回答:

この回答は受け入れられましたが、いくつかの問題があるため、より良いものに更新します。どうやら、$parse現在のスコープのプロパティにないサービスです。つまり、角度式のみを受け取り、スコープに到達できません。 {{}}式はangularjsの起動時にpostlinkコンパイルされます。つまり、ディレクティブメソッドで式にアクセスしようとすると、式は既にコンパイルされています。({{1+1}}ある2すでにディレクティブで)。

これはあなたが使いたい方法です:

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

myApp.directive('myDirective', function ($parse) {
    return function (scope, element, attr) {
        element.val("value=" + $parse(attr.myDirective)(scope));
    };
});

function MyCtrl($scope) {
    $scope.aaa = 3432;
}​

<div ng-controller="MyCtrl">
    <input my-directive="123">
    <input my-directive="1+1">
    <input my-directive="'1+1'">
    <input my-directive="aaa">
</div>​​​​​​​​

ここで注意する必要があるのは、値の文字列を設定する場合は、引用符で囲む必要があることです。(3番目の入力を参照)

ここで遊ぶフィドルです:http : //jsfiddle.net/neuTA/6/

古い答え:

私のように惑わされる可能性のある人のためにこれを削除するつもりはありません。使用$evalすることはそれを行うための正しい方法で完全に問題ありません$parseが、動作が異なることに注意してください。ほとんどの場合、これを使用する必要はないでしょう。

これを行う方法は、もう一度、を使用することscope.$evalです。角度式をコンパイルするだけでなく、現在のスコープのプロパティにもアクセスできます。

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

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+ scope.$eval(attr.value));
    }
});

function MyCtrl($scope) {

}​

あなたが欠けているのはでした$eval

http://docs.angularjs.org/api/ng.$ro​​otScope.Scope#$eval

結果を返す現在のスコープで式を実行します。式内の例外はすべて伝達されます(キャッチされません)。これは、角度式を評価するときに役立ちます。


返信ありがとうございます。ただし、これは解決策ではありません。フィドルをコードで更新しました。jsfiddle.net/neuTA/3
Shlomi Schwartz

Chromeでは、scope。$ parseを使用しようとするとこのエラーが発生します。オブジェクト#<Object>にメソッド '$ parse'がありません。$ parseサービスを挿入する場合-function($ parse){return function(scope ...-then try: "value =" + $ parse(attr.value)-it is not work for meどちらか。
マーク・Rajcok

@Markあなたは正しい、奇妙なフィドルの例(jsfiddle.net/neuTA/4)では機能しますが、私が持っているコードでは機能しません...角度バージョン?
Shlomi Schwartz 2012

2
「より良い答え」セクションで$scope.textは、リンク機能で未定義になります。現在の答えの言い方では、未定義ではないように思えます。補間された値を非同期で表示するには、$ observe()(または$ watch()が実際にここでも機能する)を使用する必要があります。私の回答とstackoverflow.com/questions/14876112/…を
Mark Rajcok

1
、「それでもOK」回答に思える$parseサービスを注入して使用されることはありません。何か不足していますか?
superjos 2013年

83

分離されたスコープを使用していないディレクティブで補間する必要がある属性値の場合、たとえば、

<input my-directive value="{{1+1}}">

属性のメソッドを使用$observe

myApp.directive('myDirective', function () {
  return function (scope, element, attr) {
    attr.$observe('value', function(actual_value) {
      element.val("value = "+ actual_value);
    })
 }
});

ディレクティブページから、

補間された属性の観察:補間$observeを含む属性の値の変化を観察するために使用します(例:)src="{{bar}}"。これは非常に効率的であるだけでなく、実際の値を簡単に取得する唯一の方法でもあります。これは、リンク段階では補間がまだ評価されていないため、この時点では値がに設定されているためundefinedです。

属性値が単なる定数の場合、たとえば、

<input my-directive value="123">

値が数値またはブール値で、正しい型が必要な場合は、$ evalを使用できます。

return function (scope, element, attr) {
   var number = scope.$eval(attr.value);
   console.log(number, number + 1);
});

属性値が文字列定数である場合、または値をディレクティブで文字列型にしたい場合は、直接アクセスできます。

return function (scope, element, attr) {
   var str = attr.value;
   console.log(str, str + " more");
});

ただし、この場合は、補間された値と定数をサポートする必要があるため、を使用します$observe


これがあなたが見つけた唯一の解決策でしたか?
Shlomi Schwartz 2012

4
はい、そしてディレクティブページはこのアプローチを推奨しているので、これが私がそうする方法です。
Mark Rajcok 2012

7
+1、これはディレクティブのスコープを強制せず、$ observeで属性の変更もカバーするため、最良の回答のIMOです
BiAiB

4

ここでの他の答えは非常に正確で価値があります。しかし、単純なものが必要な場合もあります。更新を必要とせず、分離スコープをいじることなく、ディレクティブのインスタンス化でプレーンな古い解析値を取得します。たとえば、宣言的なペイロードを次の形式で配列またはハッシュオブジェクトとしてディレクティブに提供すると便利です。

my-directive-name="['string1', 'string2']"

その場合、あなたは追跡に切り替わり、素敵なベーシックを使用することができますangular.$eval(attr.attrName)

element.val("value = "+angular.$eval(attr.value));

ワーキングフィドル


古い角度バージョンを使用したかどうかはわかりませんが、すべてのコードサンプルが無効なjavascript(my-directive-name =)または無効な角度(angular。$ evalが存在しない)であるため、-1です
BiAiB

うーん...この投稿が1年以上前のものであることを考えると、何かが廃止されたとしても、それはまったく驚くことではありません。ただし、10秒のGoogle検索で$ evalに関する多くの資料が見つかります(ここSOを含む)。そして、あなたが引用する他の例は、JavaScriptではなくHTMLでの呼び出しです。
XML

$ scope。$ eval(attr.val)は角度1.4で動作します。ディレクティブリンク関数に$ scopeを挿入する必要があります。
Martin Connell

4

私が探していた同じ解決策についてAngularjs directive with ng-Model
これは問題を解決するコードです。

    myApp.directive('zipcodeformatter', function () {
    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, element, attrs, ngModel) {

            scope.$watch(attrs.ngModel, function (v) {
                if (v) {
                    console.log('value changed, new value is: ' + v + ' ' + v.length);
                    if (v.length > 5) {
                        var newzip = v.replace("-", '');
                        var str = newzip.substring(0, 5) + '-' + newzip.substring(5, newzip.length);
                        element.val(str);

                    } else {
                        element.val(v);
                    }

                }

            });

        }
    };
});


HTML DOM

<input maxlength="10" zipcodeformatter onkeypress="return isNumberKey(event)" placeholder="Zipcode" type="text" ng-readonly="!checked" name="zipcode" id="postal_code" class="form-control input-sm" ng-model="patient.shippingZipcode" required ng-required="true">


私の結果は:

92108-2223

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

myApp .directive('myDirective', function ($timeout) {
    return function (scope, element, attr) {
        $timeout(function(){
            element.val("value = "+attr.value);
        });

    }
});

function MyCtrl($scope) {

}

変更が適用されないように、domの読み込み後にディレクティブが呼び出されるため、$ timeoutを使用します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.