Tùy chỉnh mẫu trong một Chỉ thị


98

Tôi có một biểu mẫu đang sử dụng đánh dấu từ Bootstrap, như sau:

<form class="form-horizontal">
  <fieldset>
    <legend>Legend text</legend>
    <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
        <p class="help-block">Supporting help text</p>
      </div>
    </div>
  </fieldset>
</form>

Có rất nhiều mã soạn sẵn trong đó, mà tôi muốn giảm xuống thành một chỉ thị mới - form-input, như sau:

<form-input label="Name" form-id="nameInput"></form-input>

tạo ra:

   <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
      </div>
    </div>

Tôi có nhiều thứ này làm việc thông qua một mẫu đơn giản.

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {
                label: 'bind',
                formId: 'bind'
            },
            template:   '<div class="control-group">' +
                            '<label class="control-label" for="{{formId}}">{{label}}</label>' +
                            '<div class="controls">' +
                                '<input type="text" class="input-xlarge" id="{{formId}}" name="{{formId}}">' +
                            '</div>' +
                        '</div>'

        }
    })

Tuy nhiên, khi tôi bổ sung thêm các chức năng nâng cao thì tôi lại gặp khó khăn.

Làm cách nào để hỗ trợ các giá trị mặc định trong mẫu?

Tôi muốn hiển thị tham số "type" dưới dạng thuộc tính tùy chọn trên chỉ thị của mình, ví dụ:

<form-input label="Password" form-id="password" type="password"/></form-input>
<form-input label="Email address" form-id="emailAddress" type="email" /></form-input>

Tuy nhiên, nếu không có gì được chỉ định, tôi muốn để mặc định thành "text". Tôi có thể hỗ trợ điều này như thế nào?

Làm cách nào để tùy chỉnh mẫu dựa trên sự hiện diện / vắng mặt của các thuộc tính?

Tôi cũng muốn có thể hỗ trợ thuộc tính "bắt buộc", nếu thuộc tính này có mặt. Ví dụ:

<form-input label="Email address" form-id="emailAddress" type="email" required/></form-input>

Nếu requiredcó trong chỉ thị, tôi muốn thêm nó vào giá trị được tạo <input />trong đầu ra và bỏ qua nó nếu không. Tôi không chắc làm thế nào để đạt được điều này.

Tôi nghi ngờ những yêu cầu này có thể đã vượt ra ngoài một khuôn mẫu đơn giản và phải bắt đầu sử dụng giai đoạn biên dịch trước, nhưng tôi không biết bắt đầu từ đâu.


Tôi có phải là người duy nhất nhìn thấy con voi trong phòng không :) -> Điều gì sẽ xảy ra nếu typeđược thiết lập động thông qua ràng buộc vd. type="{{ $ctrl.myForm.myField.type}}"? Tôi đã kiểm tra tất cả các phương pháp bên dưới và không thể tìm thấy bất kỳ giải pháp nào sẽ hoạt động trong trường hợp này. Có vẻ như hàm mẫu sẽ thấy các giá trị theo nghĩa đen của các thuộc tính, ví dụ. tAttr['type'] == '{{ $ctrl.myForm.myField.type }}'thay vì tAttr['type'] == 'password'. Tôi bị bối rối.
Dimitry K

Câu trả lời:


211
angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        compile: function(element, attrs) {
            var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
            element.replaceWith(htmlText);
        }
    };
})

6
Điều này hơi muộn, nhưng nếu htmlTextbạn đã thêm một ng-clicknơi nào đó, liệu sửa đổi duy nhất là thay thế element.replaceWith(htmlText)bằng element.replaceWith($compile(htmlText))?
jclancy

@Misko, bạn đã đề cập để loại bỏ phạm vi. Tại sao? Tôi có một chỉ thị không biên dịch khi được sử dụng với phạm vi bị cô lập.
Syam

1
điều này không hoạt động nếu htmlTextchứa chỉ thị ng-transclude
Alps

3
Thật không may, tôi nhận thấy rằng xác thực biểu mẫu dường như không hoạt động với điều này, các $errorcờ trên đầu vào được chèn không bao giờ được đặt. Tôi đã phải làm điều này trong thuộc tính liên kết của chỉ thị: $compile(htmlText)(scope,function(_el){ element.replaceWith(_el); });để trình điều khiển của biểu mẫu nhận ra sự tồn tại mới được hình thành của nó và đưa nó vào xác thực. Tôi không thể làm cho nó hoạt động trong thuộc tính biên dịch của chỉ thị.
meconroy

5
Được rồi, đó là năm 2015 và tôi khá chắc chắn rằng có điều gì đó sai nghiêm trọng trong việc tạo đánh dấu trong tập lệnh theo cách thủ công .
BorisOkunskiy

38

Đã cố gắng sử dụng giải pháp do Misko đề xuất, nhưng trong tình huống của tôi, một số thuộc tính, cần được hợp nhất vào html mẫu của tôi, tự chúng là chỉ thị.

Thật không may, không phải tất cả các lệnh được tham chiếu bởi mẫu kết quả đều hoạt động chính xác. Tôi không có đủ thời gian để đi sâu vào mã góc cạnh và tìm ra nguyên nhân gốc rễ, nhưng đã tìm ra cách giải quyết, có thể hữu ích.

Giải pháp là di chuyển mã, tạo ra html mẫu, từ biên dịch sang một chức năng mẫu. Ví dụ dựa trên mã từ trên:

    angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        template: function(element, attrs) {
           var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
             return htmlText;
        }
        compile: function(element, attrs)
        {
           //do whatever else is necessary
        }
    }
})

Điều này giải quyết vấn đề của tôi với một ng nhấp chuột nhúng trong mẫu
joshcomley

Cảm ơn, điều này đã làm việc cho tôi. Muốn bọc một chỉ thị để áp dụng một số thuộc tính mặc định.
martinoss

2
Cảm ơn, tôi thậm chí còn không biết rằng mẫu đã chấp nhận một chức năng!
Jon Snow

2
Đây không phải là một giải pháp thay thế. Đó là câu trả lời đúng cho OP. Việc tạo mẫu có điều kiện tùy thuộc vào các thuộc tính của phần tử là mục đích chính xác của hàm mẫu chỉ thị / thành phần. Bạn không nên sử dụng trình biên dịch cho điều đó. Nhóm Angular đang rất khuyến khích phong cách viết mã này (không sử dụng hàm biên dịch).
jose.angel.jimenez

Điều này sẽ là câu trả lời đúng, thậm chí tôi còn không biết mẫu có hàm :)
NeverGiveUp161

5

Các câu trả lời trên rất tiếc là không hoàn toàn hoạt động. Đặc biệt, giai đoạn biên dịch không có quyền truy cập vào phạm vi, vì vậy bạn không thể tùy chỉnh trường dựa trên các thuộc tính động. Sử dụng giai đoạn liên kết dường như mang lại sự linh hoạt nhất (về mặt tạo dom không đồng bộ, v.v.) Phương pháp tiếp cận dưới đây giải quyết rằng:

<!-- Usage: -->
<form>
  <form-field ng-model="formModel[field.attr]" field="field" ng-repeat="field in fields">
</form>
// directive
angular.module('app')
.directive('formField', function($compile, $parse) {
  return { 
    restrict: 'E', 
    compile: function(element, attrs) {
      var fieldGetter = $parse(attrs.field);

      return function (scope, element, attrs) {
        var template, field, id;
        field = fieldGetter(scope);
        template = '..your dom structure here...'
        element.replaceWith($compile(template)(scope));
      }
    }
  }
})

Tôi đã tạo một ý chính với mã hoàn chỉnh hơn và một writeup của phương pháp này.


cách tiếp cận tốt. không may khi sử dụng với ngTransclude tôi nhận được lỗi sau:Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found.
Alp

và tại sao không sử dụng một phạm vi biệt lập với 'field: "="'?
IttayD

Rất tốt, cảm ơn! Rất tiếc, phương pháp tiếp cận bằng văn bản của bạn đang ngoại tuyến :(
Michiel

Cả ý chính và viết lên đều là các liên kết chết.
binki, 13:18

4

Đây là những gì tôi đã sử dụng.

Tôi còn rất mới với AngularJS, vì vậy rất muốn xem các giải pháp thay thế / tốt hơn.

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {},
            link: function(scope, element, attrs)
            {
                var type = attrs.type || 'text';
                var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
                var htmlText = '<div class="control-group">' +
                    '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                        '<div class="controls">' +
                        '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                        '</div>' +
                    '</div>';
                element.html(htmlText);
            }
        }
    })

Ví dụ sử dụng:

<form-input label="Application Name" form-id="appName" required/></form-input>
<form-input type="email" label="Email address" form-id="emailAddress" required/></form-input>
<form-input type="password" label="Password" form-id="password" /></form-input>

10
Một giải pháp tốt hơn là: (1) sử dụng một hàm biên dịch thay vì liên kết hàm và thực hiện thay thế ở đó. Mẫu sẽ không hoạt động trong trường hợp của bạn vì bạn muốn tùy chỉnh nó. (2) thoát khỏi phạm vi:
Misko Hevery

@MiskoHevery Cảm ơn bạn đã phản hồi - bạn có vui lòng giải thích lý do tại sao một hàm biên dịch được ưu tiên cho một hàm liên kết ở đây không?
Marty Pitt

4
Tôi nghĩ đây là câu trả lời, từ docs.angularjs.org/guide/directive : "Bất kỳ thao tác nào có thể được chia sẻ giữa các phiên bản của chỉ thị [ví dụ: chuyển đổi mẫu DOM] nên được chuyển sang hàm biên dịch vì lý do hiệu suất."
Mark Rajcok

@Marty Bạn vẫn có thể liên kết một trong các đầu vào tùy chỉnh của mình với một mô hình chứ? (tức là. <form-input ng-model="appName" label="Application Name" form-id="appName" required/></form-input>)
Jonathan Wilson

1
@MartyPitt Từ cuốn sách "AngularJS" của O'Reilly: "Vì vậy, chúng tôi đã có compilegiai đoạn xử lý việc chuyển đổi mẫu và linkgiai đoạn xử lý việc sửa đổi dữ liệu trong chế độ xem. Dọc theo những dòng này, sự khác biệt chính giữa compilevà các linkhàm trong chỉ thị là các compilehàm xử lý việc chuyển đổi chính khuôn mẫu và các linkhàm xử lý việc tạo kết nối động giữa mô hình và chế độ xem. Trong giai đoạn thứ hai này, phạm vi được gắn với các linkhàm đã biên dịch và chỉ thị trở nên trực tiếp thông qua liên kết dữ liệu "
Julian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.