Cara mendapatkan atribut yang dievaluasi di dalam arahan khusus

363

Saya mencoba mendapatkan atribut yang dievaluasi dari arahan khusus saya, tetapi saya tidak dapat menemukan cara yang tepat untuk melakukannya.

Saya telah membuat jsFiddle ini untuk diuraikan.

<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);
    }
});

Apa yang saya lewatkan?

Shlomi Schwartz
sumber
Anda dapat mengikuti tautan di bawah ini untuk pemahaman yang lebih baik tentang arahan. undefinednull.com/2014/02/11/…
Prasanna Sasne

Jawaban:

573

Perhatikan: Saya memperbarui jawaban ini karena saya menemukan solusi yang lebih baik. Saya juga menyimpan jawaban lama untuk referensi di masa mendatang asalkan tetap terkait. Jawaban terbaru dan terbaik didahulukan.

Jawaban yang lebih baik:

Arahan dalam angular sangat kuat, tetapi perlu waktu untuk memahami proses yang ada di belakangnya.

Saat membuat arahan, angularjs memungkinkan Anda untuk membuat ruang lingkup yang terisolasi dengan beberapa ikatan pada ruang lingkup induk. Binding ini ditentukan oleh atribut yang Anda lampirkan elemen di DOM dan bagaimana Anda mendefinisikan properti lingkup dalam objek definisi direktif .

Ada 3 jenis opsi pengikatan yang dapat Anda tetapkan dalam ruang lingkup dan Anda menuliskannya sebagai atribut terkait awalan.

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>

Dalam hal ini, dalam lingkup arahan (apakah itu dalam fungsi penghubung atau pengontrol), kita dapat mengakses properti ini seperti ini:

/* 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 .

"Masih OK" Jawab:

Karena jawaban ini diterima, tetapi memiliki beberapa masalah, saya akan memperbaruinya ke yang lebih baik. Rupanya, $parseadalah layanan yang tidak terletak pada properti lingkup saat ini, yang berarti hanya mengambil ekspresi sudut dan tidak dapat mencapai ruang lingkup. {{, }}ekspresi dikompilasi ketika angular memulai, yang berarti ketika kita mencoba mengaksesnya dalam postlinkmetode arahan kita , mereka sudah dikompilasi. ( {{1+1}}sudah 2dalam arahan).

Inilah yang ingin Anda gunakan:

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>​​​​​​​​

Satu hal yang harus Anda perhatikan di sini adalah, jika Anda ingin mengatur nilai string, Anda harus membungkusnya dengan tanda kutip. (Lihat input ke-3)

Inilah biola yang bisa dimainkan: http://jsfiddle.net/neuTA/6/

Jawaban lama:

Saya tidak menghapus ini untuk orang-orang yang dapat disesatkan seperti saya, perhatikan bahwa menggunakan $evalsangat baik cara yang benar untuk melakukannya, tetapi $parsememiliki perilaku yang berbeda, Anda mungkin tidak akan memerlukan ini untuk digunakan dalam sebagian besar kasus.

Cara untuk melakukannya adalah, sekali lagi, menggunakan scope.$eval. Tidak hanya mengkompilasi ekspresi sudut, ia juga memiliki akses ke properti lingkup saat ini.

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

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

function MyCtrl($scope) {

}​

Apa yang Anda lewatkan adalah $eval.

http://docs.angularjs.org/api/ng.$rootScope.Scope#$eval

Menjalankan ekspresi pada lingkup saat ini untuk mengembalikan hasilnya. Setiap pengecualian dalam ekspresi disebarkan (tidak tertangkap). Ini berguna ketika mengevaluasi ekspresi sudut.

Umur Kontacı
sumber
Terima kasih atas jawabannya, namun ini bukan solusinya. Saya telah memperbarui biola dengan kode Anda. jsfiddle.net/neuTA/3
Shlomi Schwartz
Di Chrome saya mendapatkan kesalahan ini ketika mencoba menggunakan lingkup. $ Parse: Object # <Object> tidak memiliki metode '$ parse'. Jika saya menyuntikkan layanan $ parse - function ($ parse) {function return (lingkup ... - lalu coba: "value =" + $ parse (attr.value) - yang sepertinya tidak bekerja untuk saya baik
Mark Rajcok
@Mark Anda benar, aneh itu berfungsi dalam contoh biola ( jsfiddle.net/neuTA/4 ) tetapi tidak dalam kode yang saya miliki ... versi sudut?
Shlomi Schwartz
2
Di bagian "Jawaban yang lebih baik", $scope.textakan ditentukan dalam fungsi penautan. Cara jawabannya saat ini worded, sepertinya itu tidak akan terdefinisi. Anda harus menggunakan $ mengamati () (atau $ menonton () akan benar-benar bekerja di sini juga) untuk secara asinkron melihat nilai yang diinterpolasi. Lihat jawaban saya dan juga stackoverflow.com/questions/14876112/…
Mark Rajcok
1
Dalam "Masih OK" Jawab sepertinya $parselayanan disuntikkan dan kemudian tidak pernah digunakan. Apakah saya melewatkan sesuatu?
superjos
83

Untuk nilai atribut yang perlu diinterpolasi dalam arahan yang tidak menggunakan lingkup terisolasi, misalnya,

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

gunakan metode Atribut $observe:

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

Dari halaman arahan ,

mengamati atribut interpolasi: Gunakan $observeuntuk mengamati perubahan nilai atribut yang mengandung interpolasi (misalnya src="{{bar}}"). Tidak hanya ini sangat efisien tetapi juga satu-satunya cara untuk dengan mudah mendapatkan nilai aktual karena selama fase penautan interpolasi belum dievaluasi dan nilainya saat ini diatur ke undefined.

Jika nilai atribut hanya konstanta, misalnya,

<input my-directive value="123">

Anda dapat menggunakan $ eval jika nilainya adalah angka atau boolean, dan Anda menginginkan jenis yang benar:

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

Jika nilai atribut adalah konstanta string, atau Anda ingin nilai tersebut menjadi tipe string dalam arahan Anda, Anda dapat mengaksesnya secara langsung:

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

Namun, dalam kasus Anda, karena Anda ingin mendukung nilai dan konstanta yang diinterpolasi, gunakan $observe.

Mark Rajcok
sumber
apakah ini satu-satunya solusi yang Anda temukan?
Shlomi Schwartz
4
Ya, dan karena halaman petunjuk merekomendasikan pendekatan ini, ini adalah bagaimana saya akan melakukannya.
Mark Rajcok
7
+1, ini adalah jawaban IMO terbaik karena tidak memaksakan ruang lingkup pada direktif dan juga mencakup perubahan atribut dengan $ observ
BiAiB
4

Jawaban lain di sini sangat benar, dan berharga. Tetapi kadang-kadang Anda hanya ingin sederhana: untuk mendapatkan nilai parsing lama sederhana di instantiation direktif, tanpa perlu pembaruan, dan tanpa mengacaukan ruang lingkup yang terisolasi. Misalnya, dapat berguna untuk menyediakan muatan deklaratif ke dalam direktif Anda sebagai array atau objek-hash dalam bentuk:

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

Dalam hal ini, Anda dapat memotong ke pengejaran dan hanya menggunakan dasar yang bagus angular.$eval(attr.attrName).

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

Biola yang Bekerja .

XML
sumber
Saya tidak tahu apakah Anda menggunakan versi sudut lama atau tidak, tetapi semua sampel kode Anda javascript tidak valid (my-directive-name =) atau tidak valid sudut (sudut. $ Eval tidak ada), jadi -1
BiAiB
Ummm ... mengingat bahwa postingan ini berumur lebih dari satu tahun, tidak akan mengherankan jika ada sesuatu yang sudah usang. Namun, pencarian Google 10 detik akan menemukan Anda banyak materi tentang $ eval, termasuk di sini di SO . Dan contoh lain yang Anda kutip adalah doa dalam HTML, bukan Javascript.
XML
$ scope. $ eval (attr.val) bekerja dalam sudut 1.4. Membutuhkan $ scope untuk disuntikkan ke fungsi directive link.
Martin Connell
4

Untuk solusi yang sama yang saya cari Angularjs directive with ng-Model.
Berikut adalah kode yang menyelesaikan masalah.

    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">


Hasil saya adalah:

92108-2223
Satish Singh
sumber
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) {

}

Gunakan $ timeout karena arahan panggilan setelah dom memuat sehingga perubahan Anda tidak berlaku

pengguna1693371
sumber