AngularJS - Buat arahan yang menggunakan ng-model

294

Saya mencoba membuat arahan yang akan membuat bidang input dengan model-ng yang sama dengan elemen yang membuat arahan.

Inilah yang saya buat sejauh ini:

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

Namun, saya tidak yakin ini adalah cara yang tepat untuk menangani skenario ini, dan ada bug yang kontrol saya tidak diinisialisasi dengan nilai bidang target ng-model.

Berikut adalah Plunker dari kode di atas: http://plnkr.co/edit/IvrDbJ

Apa cara penanganan yang benar?

EDIT : Setelah menghapus ng-model="value"dari templat, ini tampaknya berfungsi dengan baik. Namun, saya akan tetap membuka pertanyaan ini karena saya ingin memeriksa ulang apakah ini cara yang tepat untuk melakukan ini.

kolrie
sumber
1
Bagaimana jika Anda menghapus scopedan mengaturnya scope: false? Bagaimana cara mengikat ng-modeldalam kasus itu?
Saeed Neamati

Jawaban:

210

EDIT : Jawaban ini sudah tua dan kemungkinan kedaluwarsa. Hanya kepala sehingga tidak membuat orang tersesat. Saya tidak lagi menggunakan Angular jadi saya tidak dalam posisi yang baik untuk melakukan perbaikan.


Ini sebenarnya logika yang cukup bagus tetapi Anda dapat menyederhanakan banyak hal.

Pengarahan

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 dengan arahan

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

Anda dapat melihatnya beraksi dengan Plunker ini .

Inilah yang saya lihat:

  • Saya mengerti mengapa Anda ingin menggunakan 'ng-model' tetapi dalam kasus Anda itu tidak perlu. ng-model adalah untuk menautkan elemen html yang ada dengan nilai dalam ruang lingkup. Karena Anda membuat arahan sendiri, Anda membuat elemen html 'baru', jadi Anda tidak perlu ng-model.

EDIT Seperti yang disebutkan oleh Mark dalam komentarnya, tidak ada alasan bahwa Anda tidak dapat menggunakan ng-model, hanya untuk mengikuti konvensi.

  • Dengan secara eksplisit membuat ruang lingkup dalam direktif Anda (ruang lingkup 'terisolasi'), ruang lingkup direktif tidak dapat mengakses variabel 'nama' pada ruang lingkup induk (itulah sebabnya, saya pikir, Anda ingin menggunakan model-ng).
  • Saya menghapus ngModel dari arahan Anda dan menggantinya dengan nama khusus yang dapat Anda ubah menjadi apa pun.
  • Hal yang membuat semuanya masih berfungsi adalah tanda '=' di lingkup. Lihat dokumen docs di bawah tajuk 'lingkup'

Secara umum, arahan Anda harus menggunakan lingkup terisolasi (yang Anda lakukan dengan benar) dan menggunakan lingkup tipe '=' jika Anda ingin nilai dalam arahan Anda untuk selalu memetakan ke nilai dalam lingkup induk.

Roy Truelove
sumber
18
+1, tapi saya tidak yakin saya setuju dengan pernyataan "ng-model adalah untuk menautkan elemen HTML yang ada dengan nilai dalam ruang lingkup." Dua contenteditablecontoh arahan dalam Angular docs - halaman formulir , halaman NgModelController - keduanya menggunakan ng-model. Dan halaman ngModelController mengatakan bahwa pengontrol ini "dimaksudkan untuk diperluas oleh arahan lain."
Mark Rajcok
33
Saya tidak yakin mengapa jawaban ini dinilai sangat tinggi karena tidak memenuhi apa yang ditanyakan oleh pertanyaan awal - yaitu menggunakan ngModel. Ya, seseorang dapat menghindari penggunaan ngModel dengan meletakkan status pada pengendali induk tetapi hal ini mengorbankan dua pengendali yang terikat erat dan tidak dapat menggunakan / menggunakannya kembali secara mandiri. Ini seperti menggunakan variabel global alih-alih mengatur pendengar antara dua komponen - secara teknis mungkin lebih sederhana tetapi itu bukan solusi yang baik dalam banyak kasus.
Pat Niemeyer
Saya akan menambahkan bahwa jika dia ingin mengandalkan pengontrol induk, dia harus menyuntikkannya dengan 'memerlukan: ^ induk' - sehingga dia dapat membuat dependensi eksplisit dan opsional jika diinginkan.
Pat Niemeyer
3
@ Joenen Cara saya melihatnya manfaat utama adalah konsistensi dengan tempat lain di mana model disahkan sebagai hg-model(dan bukan masalah kopling, IMO). Dengan cara ini konteks data selalu menggunakan ng-model apakah itu adalah <input>atau direktif kustom, sehingga menyederhanakan overhead kognitif untuk penulis HTML. Yaitu menghemat penulis HTML harus mencari tahu apa nama my-directive-varuntuk setiap arahan, terutama karena tidak ada pelengkapan otomatis untuk membantu Anda.
zai chang
2
umm ... ok ... tapi sekarang ini tidak lagi berfungsi dengan ng-model-optionsatau semua model lainnya, bukan?
George Mauer
68

Saya mengambil kombinasi dari semua jawaban, dan sekarang memiliki dua cara untuk melakukan ini dengan atribut ng-model:

  • Dengan ruang lingkup baru yang menyalin ngModel
  • Dengan cakupan yang sama yang melakukan kompilasi pada tautan

Saya tidak yakin saya suka kompilasi saat tautan. Namun, jika Anda hanya mengganti elemen dengan yang lain Anda tidak perlu melakukan itu.

Secara keseluruhan, saya lebih suka yang pertama. Cukup setel cakupan ke {ngModel:"="}dan atur di ng-model="ngModel"mana Anda inginkan dalam template Anda.

Pembaruan : Saya menyisipkan potongan kode dan memperbaruinya untuk Angular v1.2. Ternyata ruang lingkup isolasi masih terbaik, terutama ketika tidak menggunakan jQuery. Jadi intinya adalah:

  • Apakah Anda mengganti satu elemen: Ganti saja, biarkan ruang lingkupnya saja, tetapi perhatikan bahwa ganti tidak digunakan untuk v2.0:

    app.directive('myReplacedDirective', function($compile) {
      return {
        restrict: 'E',
        template: '<input class="some">',
        replace: true
      };
    });
  • Kalau tidak gunakan ini:

    app.directive('myDirectiveWithScope', function() {
      return {
        restrict: 'E',
        scope: {
          ngModel: '=',
        },
        template: '<div class="some"><input ng-model="ngModel"></div>'
      };
    });
w00t
sumber
1
Saya memperbarui plunker dengan ketiga kemungkinan ruang lingkup dan untuk elemen turunan templat atau elemen dasar templat.
w00t
1
Ini bagus, tetapi bagaimana Anda pada dasarnya membuat ini opsional? Saya sedang membuat arahan kotak teks untuk perpustakaan UI, dan saya ingin model menjadi opsional, artinya kotak teks masih akan berfungsi jika ngModel tidak disetel.
Nick Radford
1
@NickRadford Cukup periksa apakah ngModel didefinisikan pada $ scope dan jika tidak, jangan gunakan itu?
w00t
1
Apakah akan ada masalah atau overhead tambahan dengan menggunakan kembali ng-modeldalam ruang lingkup yang terisolasi?
Jeff Ling
2
Aku tidak tahu pasti, tapi kurasa tidak. Menyalin ngModel sangat ringan dan cakupan ruang lingkup yang terisolasi terbatas.
w00t
52

tidak begitu rumit: dalam perintah Anda, gunakan alias: scope:{alias:'=ngModel'}

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

di html Anda, gunakan seperti biasa

<dateselect ng-model="birthday"></dateselect>
AiShiguang
sumber
1
Ini jauh lebih mudah ketika berhadapan dengan perpustakaan seperti Kendo UI. Terima kasih!
bytebender
30

Anda hanya perlu ng-model ketika Anda perlu mengakses $ viewValue atau $ modelValue model. Lihat NgModelController . Dan dalam hal ini, Anda akan menggunakannya require: '^ngModel'.

Untuk selebihnya, lihat jawaban Roys .

Asgoth
sumber
2
ng-model juga berguna bahkan jika Anda tidak memerlukan $ viewValue atau $ modelValue. Ini berguna bahkan jika Anda hanya menginginkan fitur pengikatan data dari model-ng, seperti contoh @ kolrie.
Mark Rajcok
1
Dan ^harus ada hanya jika model-ng diterapkan dalam elemen induk
georgios
18

Ini adalah jawaban yang sedikit terlambat, tapi saya menemukan mengagumkan ini posting tentang NgModelController, yang saya pikir adalah apa yang Anda cari.

TL; DR - Anda dapat menggunakan require: 'ngModel'dan kemudian menambahkan NgModelControllerke fungsi penautan Anda:

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

Dengan cara ini, tidak perlu diretas - Anda menggunakan bawaan Angular ng-model

Yaniv Efraim
sumber
2

Saya tidak akan mengatur ngmodel melalui atribut, Anda dapat menentukannya dengan benar di template:

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

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

Mathew Berg
sumber
0

Karena Angular 1.5 dimungkinkan untuk menggunakan Komponen. Komponen adalah cara untuk pergi dan menyelesaikan masalah ini dengan mudah.

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

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

Di dalam YourController, yang perlu Anda lakukan adalah:

this.ngModel = "x"; //$scope.$apply("$ctrl.ngModel"); if needed
Niels Steenbeek
sumber
Apa yang saya temukan adalah itu berfungsi jika Anda memang menggunakan "=" daripada "<" yang merupakan praktik terbaik menggunakan Komponen. Saya tidak yakin apa arti "inside YourController" dari jawaban ini, maksudnya bukan untuk mengatur ngModel di dalam komponen?
Marc Stober
1
@ MarsCober Dengan "inside YourController" Saya hanya ingin menunjukkan bahwa ngModel tersedia sebagai pengambil dan penyetel. Dalam contoh ini $ ctrl.result akan menjadi "x".
Niels Steenbeek
Baik. Saya pikir bagian lain yang penting adalah Anda juga bisa, dalam template controller Anda, lakukan input ng-model="$ctrl.ngModel"dan akan disinkronkan dengan $ ctrl.result juga.
Marc Stober
0

Membuat ruang lingkup isolat tidak diinginkan. Saya akan menghindari menggunakan atribut lingkup dan melakukan sesuatu seperti ini. scope: true memberi Anda lingkup anak baru tetapi tidak mengisolasi. Kemudian gunakan parse untuk mengarahkan variabel lingkup lokal ke objek yang sama yang telah disediakan pengguna ke atribut 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);
        }
    };
}]);
btm1
sumber