คำสั่ง AngularJS พร้อมตัวเลือกเริ่มต้น


145

ฉันเพิ่งเริ่มต้นด้วย angularjs และกำลังทำงานเพื่อแปลงปลั๊กอิน JQuery เก่าสองสามตัวเป็นแนวทางเชิงมุม ฉันต้องการกำหนดชุดตัวเลือกเริ่มต้นสำหรับคำสั่งของฉัน (องค์ประกอบ) ซึ่งสามารถแทนที่ได้โดยการระบุค่าตัวเลือกในแอตทริบิวต์

ฉันได้ดูวิธีที่คนอื่นทำเช่นนี้และในห้องสมุดเชิงมุม uiดูเหมือนว่าui.bootstrap.paginationจะทำสิ่งที่คล้ายกัน

ก่อนอื่นตัวเลือกเริ่มต้นทั้งหมดจะถูกกำหนดไว้ในวัตถุคงที่:

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

จากนั้นgetAttributeValueฟังก์ชั่นยูทิลิตี้จะแนบกับตัวควบคุมคำสั่ง:

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

ในที่สุดนี้จะใช้ในฟังก์ชั่นการเชื่อมโยงเพื่ออ่านในคุณลักษณะเป็น

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

ดูเหมือนจะเป็นการตั้งค่าที่ค่อนข้างซับซ้อนสำหรับบางสิ่งที่เป็นมาตรฐานซึ่งต้องการแทนที่ชุดของค่าเริ่มต้น มีวิธีอื่นในการทำเช่นนี้ที่เป็นเรื่องปกติหรือไม่? หรือเป็นเรื่องปกติที่จะกำหนดฟังก์ชั่นยูทิลิตี้เช่นgetAttributeValueและตัวเลือกในการแยกวิเคราะห์ด้วยวิธีนี้หรือไม่? ฉันสนใจที่จะหาว่าคนอื่น ๆ มีกลยุทธ์อะไรสำหรับงานทั่วไปนี้

นอกจากนี้ฉันยังไม่ชัดเจนว่าทำไมinterpolateต้องใช้พารามิเตอร์

คำตอบ:


108

คุณสามารถใช้compileฟังก์ชั่น - อ่านคุณสมบัติถ้าพวกเขาไม่ได้ตั้ง - เติมพวกเขาด้วยค่าเริ่มต้น

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

1
ขอบคุณ! ดังนั้นความคิดใด ๆ ที่ui.bootstrap.paginationทำให้สิ่งต่าง ๆ มีความซับซ้อนมากขึ้น? กำลังคิดว่าถ้าใช้ฟังก์ชั่นคอมไพล์การเปลี่ยนแปลงคุณสมบัติใด ๆ ที่ทำในภายหลังจะไม่ได้รับผลกระทบ แต่สิ่งนี้ดูเหมือนจะไม่เป็นจริงเพราะมีเพียงค่าเริ่มต้นเท่านั้นที่ตั้งค่าไว้ในขั้นตอนนี้ คิดว่าจะต้องมีการแลกเปลี่ยนบางอย่างที่นี่
Ken Chatfield

3
@KenChatfield ในcompileคุณไม่สามารถอ่านคุณสมบัติซึ่งควรจะถูกแก้ไขเพื่อรับค่า (ซึ่งมีการแสดงออก) แต่ถ้าคุณต้องการตรวจสอบเฉพาะว่าแอตทริบิวต์ว่างเปล่ามันจะทำงานโดยไม่มีการแลกเปลี่ยนใด ๆ สำหรับคุณ (ก่อนแอตทริบิวต์การแก้ไขจะมีสตริงที่มีการแสดงออก)
OZ_

1
Fantastic! ขอบคุณมากสำหรับคำอธิบายที่ชัดเจนของคุณ สำหรับผู้อ่านในอนาคตแม้ว่าจะสัมผัสกับคำถามเดิม แต่สำหรับคำอธิบายว่าพารามิเตอร์ 'interpolate' ทำในui.bootstrap.paginationตัวอย่างฉันพบตัวอย่างที่มีประโยชน์มากนี้: jsfiddle.net/EGfgH
Ken Chatfield

ขอบคุณมากสำหรับการแก้ปัญหา โปรดทราบว่าหากคุณต้องการlinkตัวเลือกคุณยังสามารถส่งคืนฟังก์ชันในcompileตัวเลือกของคุณ เอกสารที่นี่
ปิดบัง

4
โปรดจำไว้ว่าแอตทริบิวต์นั้นต้องการค่าที่จะส่งผ่านจากเทมเพลต หากคุณกำลังผ่านอาร์เรย์มันควรจะเป็นattributes.foo = '["one", "two", "three"]'แทนattributes.foo = ["one", "two", "three"]
Dominik Ehrenberg

263

ใช้การ=?ตั้งค่าสถานะสำหรับคุณสมบัติในบล็อกขอบเขตของคำสั่ง

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

4
=?มีให้ตั้งแต่ 1.1.x
Michael Radionov

34
หากแอตทริบิวต์ของคุณสามารถยอมรับtrueหรือfalseเป็นค่าคุณจะ (ฉันคิดว่า) ต้องการใช้เช่น$scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;แทน
Paul D. Waite

22
หมายเหตุ: ทำงานได้เฉพาะกับสองทางที่มีผลผูกพันเช่นแต่ไม่ได้มีทางเดียวที่มีผลผูกพัน=? @?
Justus Romijn

20
สามารถทำได้ในเทมเพลตเท่านั้น: เทมเพลต: 'สวัสดี {{name ||] \ 'ชื่อเริ่มต้น \'}} '
Vil

4
ควรตั้งค่าเริ่มต้นในคอนโทรลเลอร์หรือในlinkฟังก์ชั่นหรือไม่? จากความเข้าใจของฉันการกำหนดในระหว่างlinkควรหลีกเลี่ยง$scope.$apply()วงจรไม่ใช่หรือ
Augustin Riedinger

1

ฉันใช้ AngularJS v1.5.10 และพบว่าpreLinkฟังก์ชันการคอมไพล์ทำงานได้ค่อนข้างดีสำหรับการตั้งค่าแอตทริบิวต์เริ่มต้น

เพียงเตือนความจำ:

  • attrsเก็บค่าแอตทริบิวต์ DOM ดิบซึ่งมักจะเป็นอย่างใดอย่างหนึ่งundefinedหรือสตริง
  • scopeเก็บ (เหนือสิ่งอื่นใด) ค่าแอตทริบิวต์ DOM แยกวิเคราะห์ตามข้อกำหนดขอบเขตแยกให้ ( =/ </ @/ ฯลฯ )

ตัวอย่างโค้ดแบบย่อ:

.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: ...
  }
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.