Arahan AngularJS dengan opsi default

145

Saya baru mulai dengan angularjs, dan saya sedang berusaha mengubah beberapa plugin JQuery lama menjadi arahan Angular. Saya ingin menetapkan sekumpulan opsi default untuk direktif (elemen) saya, yang dapat diganti dengan menentukan nilai opsi dalam atribut.

Saya telah melihat-lihat cara orang lain melakukan ini, dan di perpustakaan angular-ui ui.bootstrap.pagination tampaknya melakukan hal serupa.

Pertama, semua opsi default didefinisikan dalam objek konstan:

.constant('paginationConfig', {
  itemsPerPage: 10,
  boundaryLinks: false,
  ...
})

Kemudian getAttributeValuefungsi utilitas dilampirkan ke pengontrol direktif:

this.getAttributeValue = function(attribute, defaultValue, interpolate) {
    return (angular.isDefined(attribute) ?
            (interpolate ? $interpolate(attribute)($scope.$parent) :
                           $scope.$parent.$eval(attribute)) : defaultValue);
};

Akhirnya, ini digunakan dalam fungsi penautan untuk membaca atribut sebagai

.directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
    ...
    controller: 'PaginationController',
    link: function(scope, element, attrs, paginationCtrl) {
        var boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks,  config.boundaryLinks);
        var firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true);
        ...
    }
});

Ini sepertinya pengaturan yang agak rumit untuk sesuatu yang standar seperti ingin mengganti satu set nilai default. Apakah ada cara lain untuk melakukan ini yang umum? Atau apakah normal untuk selalu mendefinisikan fungsi utilitas seperti getAttributeValuedan mengurai pilihan dengan cara ini? Saya tertarik untuk mengetahui strategi berbeda yang dimiliki orang untuk tugas bersama ini.

Juga, sebagai bonus, saya tidak jelas mengapa interpolateparameter diperlukan.

Ken Chatfield
sumber

Jawaban:

108

Anda dapat menggunakan compileatribut fungsi - baca jika tidak disetel - isilah dengan nilai default.

.directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
    ...
    controller: 'PaginationController',
    compile: function(element, attrs){
       if (!attrs.attrOne) { attrs.attrOne = 'default value'; }
       if (!attrs.attrTwo) { attrs.attrTwo = 42; }
    },
        ...
  }
});
ONS_
sumber
1
Terima kasih! Jadi ada pemikiran mengapa ui.bootstrap.paginationhal-hal dengan cara yang lebih rumit? Berpikir bahwa jika menggunakan fungsi kompilasi perubahan atribut apa pun yang dibuat nanti tidak akan tercermin, tetapi ini tampaknya tidak benar karena hanya default yang ditetapkan pada tahap ini. Kira harus ada tradeoff yang dilakukan di sini.
Ken Chatfield
3
@KenChatfield di compileAnda tidak dapat membaca atribut, yang harus diinterpolasi untuk mendapatkan nilai (yang berisi ekspresi). Tetapi jika Anda ingin memeriksa hanya jika atribut kosong - itu akan berfungsi tanpa pengorbanan untuk Anda (sebelum atribut interpolasi akan berisi string dengan ekspresi).
OZ_
1
Fantastis! Terima kasih banyak atas penjelasan Anda yang jelas. Untuk pembaca masa depan, meskipun bersinggungan dengan pertanyaan awal, untuk penjelasan tentang apa parameter 'interpolasi' dalam ui.bootstrap.paginationcontoh saya menemukan contoh yang sangat berguna ini: jsfiddle.net/EGfgH
Ken Chatfield
Terima kasih banyak untuk solusi itu. Perhatikan bahwa jika Anda membutuhkan linkopsi, Anda masih dapat mengembalikan fungsi di compileopsi Anda . doc di sini
mneute
4
Ingat, atribut itu membutuhkan nilainya karena akan dilewatkan dari templat. Jika Anda melewatkan array, seharusnya attributes.foo = '["one", "two", "three"]'bukanattributes.foo = ["one", "two", "three"]
Dominik Ehrenberg
263

Gunakan =?bendera untuk properti di blok lingkup direktif.

angular.module('myApp',[])
  .directive('myDirective', function(){
    return {
      template: 'hello {{name}}',
      scope: {
        // use the =? to denote the property as optional
        name: '=?'
      },
      controller: function($scope){
        // check if it was defined.  If not - set a default
        $scope.name = angular.isDefined($scope.name) ? $scope.name : 'default name';
      }
    }
  });
Pemburu
sumber
4
=?tersedia sejak 1.1.x
Michael Radionov
34
Jika atribut Anda dapat menerima trueatau falsesebagai nilai, Anda akan (saya pikir) ingin menggunakan mis $scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;.
Paul D. Waite
22
Catatan: ini hanya bekerja dengan pengikatan dua arah, misalnya =?, tetapi tidak dengan pengikatan satu arah @?,.
Justus Romijn
20
juga bisa dilakukan dalam templat saja: templat: 'hello {{name || \ 'nama default \'}} '
Vil
4
Haruskah nilai default diatur di controller atau dalam linkfungsi? Berdasarkan pemahaman saya, menugaskan selama linkharus menghindari $scope.$apply()siklus, bukan?
Augustin Riedinger
1

Saya menggunakan AngularJS v1.5.10 dan menemukan preLinkfungsi kompilasi bekerja dengan baik untuk menetapkan nilai atribut default.

Hanya pengingat:

  • attrsmemegang nilai atribut DOM mentah yang selalu berupa undefinedstring.
  • scopememegang (antara lain) nilai-nilai atribut DOM diurai sesuai dengan spesifikasi lingkup isolat yang disediakan ( =/ </ @/ dll.).

Cuplikan ringkas:

.directive('myCustomToggle', function () {
  return {
    restrict: 'E',
    replace: true,
    require: 'ngModel',
    transclude: true,
    scope: {
      ngModel: '=',
      ngModelOptions: '<?',
      ngTrueValue: '<?',
      ngFalseValue: '<?',
    },
    link: {
      pre: function preLink(scope, element, attrs, ctrl) {
        // defaults for optional attributes
        scope.ngTrueValue = attrs.ngTrueValue !== undefined
          ? scope.ngTrueValue
          : true;
        scope.ngFalseValue = attrs.ngFalseValue !== undefined
          ? scope.ngFalseValue
          : false;
        scope.ngModelOptions = attrs.ngModelOptions !== undefined
          ? scope.ngModelOptions
          : {};
      },
      post: function postLink(scope, element, attrs, ctrl) {
        ...
        function updateModel(disable) {
          // flip model value
          var newValue = disable
            ? scope.ngFalseValue
            : scope.ngTrueValue;
          // assign it to the view
          ctrl.$setViewValue(newValue);
          ctrl.$render();
        }
        ...
    },
    template: ...
  }
});
Keego
sumber