Làm cách nào tôi có thể tự động thêm một lệnh trong AngularJS?


212

Tôi có một phiên bản rất sôi nổi về những gì tôi đang làm có vấn đề.

Tôi có một đơn giản directive. Bất cứ khi nào bạn nhấp vào một yếu tố, nó sẽ thêm một yếu tố khác. Tuy nhiên, nó cần được biên dịch trước để hiển thị chính xác.

Nghiên cứu của tôi đã dẫn tôi đến $compile. Nhưng tất cả các ví dụ sử dụng một cấu trúc phức tạp mà tôi không thực sự biết cách áp dụng ở đây.

Câu đố có tại đây: http://jsfiddle.net/paulocoelho/fBjbP/1/

Và JS ở đây:

var module = angular.module('testApp', [])
    .directive('test', function () {
    return {
        restrict: 'E',
        template: '<p>{{text}}</p>',
        scope: {
            text: '@text'
        },
        link:function(scope,element){
            $( element ).click(function(){
                // TODO: This does not do what it's supposed to :(
                $(this).parent().append("<test text='n'></test>");
            });
        }
    };
});

Giải pháp của Josh David Miller: http://jsfiddle.net/paulocoelho/fBjbP/2/

Câu trả lời:


259

Bạn có rất nhiều jQuery vô nghĩa trong đó, nhưng dịch vụ $ compile thực sự siêu đơn giản trong trường hợp này:

.directive( 'test', function ( $compile ) {
  return {
    restrict: 'E',
    scope: { text: '@' },
    template: '<p ng-click="add()">{{text}}</p>',
    controller: function ( $scope, $element ) {
      $scope.add = function () {
        var el = $compile( "<test text='n'></test>" )( $scope );
        $element.parent().append( el );
      };
    }
  };
});

Bạn sẽ nhận thấy tôi đã cấu trúc lại chỉ thị của bạn để tuân theo một số thực tiễn tốt nhất. Hãy cho tôi biết nếu bạn có câu hỏi về bất kỳ trong số đó.


34
Tuyệt vời. Nó hoạt động. Hãy xem, những ví dụ đơn giản và cơ bản này là những ví dụ cần được thể hiện trong các tài liệu của angenses. Họ bắt đầu với những ví dụ phức tạp.
PCoelho

1
Cảm ơn, Josh, điều này thực sự hữu ích. Tôi đã tạo một công cụ trong Plnkr mà chúng tôi đang sử dụng trong CoderDojo mới để giúp trẻ em học cách viết mã và tôi chỉ cần mở rộng nó để bây giờ tôi có thể sử dụng các chỉ thị Angular Bootstrap như datepicker, alert, tab, v.v. và ngay bây giờ nó chỉ hoạt động trong Chrome: embed.plnkr.co/WI16H7Rsa5adejXSmyNj/preview
JoshGough

3
Josh - một cách dễ dàng hơn để thực hiện điều này mà không cần sử dụng $compile? Cảm ơn câu trả lời của bạn bằng cách này!
d doublewirve

3
@d doublewirve Trong trường hợp này, việc sử dụng ngRepeat sẽ dễ dàng hơn nhiều. :-) Nhưng tôi giả sử bạn có nghĩa là tự động thêm các chỉ thị mới vào trang, trong trường hợp đó câu trả lời là không - không có cách nào đơn giản hơn vì $compiledịch vụ là những gì nối dây chỉ thị và nối chúng vào chu kỳ sự kiện. Không có cách nào khác $compiletrong tình huống như thế này, nhưng trong hầu hết các trường hợp, một lệnh khác như ngRepeat có thể hoàn thành cùng một công việc (vì vậy ngRepeat đang thực hiện việc biên dịch cho chúng tôi). Bạn có một trường hợp sử dụng cụ thể?
Josh David Miller

2
Không nên biên dịch xảy ra trong giai đoạn prelink? Tôi nghĩ rằng bộ điều khiển chỉ nên chứa mã không phải DOM, có thể kiểm tra đơn vị, nhưng tôi chưa quen với khái niệm liên kết / bộ điều khiển nên tôi không chắc chắn về bản thân mình. Ngoài ra, một thay thế cơ bản là ng-include + part + ng-controller vì nó sẽ hoạt động như một lệnh với phạm vi được kế thừa .
Marcus Rådell

77

Ngoài ví dụ hoàn hảo của Riceball LEE về việc thêm một chỉ thị phần tử mới

newElement = $compile("<div my-directive='n'></div>")($scope)
$element.parent().append(newElement)

Thêm một chỉ thị thuộc tính mới vào phần tử tồn tại có thể được thực hiện bằng cách này:

Giả sử bạn muốn thêm nhanh my-directivechóng vào spanphần tử.

template: '<div>Hello <span>World</span></div>'

link: ($scope, $element, $attrs) ->

  span = $element.find('span').clone()
  span.attr('my-directive', 'my-directive')
  span = $compile(span)($scope)
  $element.find('span').replaceWith span

Mong rằng sẽ giúp.


3
Đừng quên xóa chỉ thị ban đầu để tránh lỗi vượt quá kích thước ngăn xếp cuộc gọi tối đa.
SRachamim

Xin chào, bạn có vui lòng cung cấp ý tưởng về API mới được đề xuất của tôi để làm cho việc lập chỉ thị lập trình trở thành một quy trình đơn giản hơn không? github.com/angular/angular.js/issues/6950 Cảm ơn!
trusktr

Tôi ước trong năm 2015 chúng tôi sẽ không có giới hạn về kích thước ngăn xếp cuộc gọi. :(
psycho brm

3
Các Maximum call stack size exceededlỗi luôn luôn xảy ra vì đệ quy vô hạn. Tôi chưa bao giờ thấy một trường hợp nào mà việc tăng kích thước ngăn xếp sẽ giải quyết nó.
Gunchars

Vấn đề tương tự tôi đang gặp phải, Bạn có thể giúp tôi ở đây stackoverflow.com/questions/38821980/ không
pandu das

45

Tự động thêm các chỉ thị trên angularjs có hai kiểu:

Thêm một lệnh angularjs vào một lệnh khác

  • chèn một phần tử mới (chỉ thị)
  • chèn một thuộc tính mới (chỉ thị) cho phần tử

chèn một phần tử mới (chỉ thị)

nó đơn giản Và bạn có thể sử dụng trong "liên kết" hoặc "biên dịch".

var newElement = $compile( "<div my-diretive='n'></div>" )( $scope );
$element.parent().append( newElement );

chèn một thuộc tính mới cho phần tử

Thật khó khăn và khiến tôi đau đầu trong vòng hai ngày.

Sử dụng "$ compile" sẽ gây ra lỗi đệ quy nghiêm trọng !! Có lẽ nó nên bỏ qua chỉ thị hiện tại khi biên dịch lại phần tử.

$element.$set("myDirective", "expression");
var newElement = $compile( $element )( $scope ); // critical recursive error.
var newElement = angular.copy(element);          // the same error too.
$element.replaceWith( newElement );

Vì vậy, tôi phải tìm cách gọi hàm "liên kết" chỉ thị. Rất khó để có được các phương thức hữu ích được ẩn sâu bên trong các bao đóng.

compile: (tElement, tAttrs, transclude) ->
   links = []
   myDirectiveLink = $injector.get('myDirective'+'Directive')[0] #this is the way
   links.push myDirectiveLink
   myAnotherDirectiveLink = ($scope, $element, attrs) ->
       #....
   links.push myAnotherDirectiveLink
   return (scope, elm, attrs, ctrl) ->
       for link in links
           link(scope, elm, attrs, ctrl)       

Bây giờ, nó hoạt động tốt.


1
Rất thích xem bản demo chèn thuộc tính mới vào phần tử, trong vanilla JS nếu có thể - Tôi đang thiếu một cái gì đó ...
Patrick

ví dụ thực tế của cách chèn một thuộc tính mới cho phần tử là đây (xem github của tôi): github.com/snowyu/angular-reactable/blob/master/src/...
Riceball LEE

1
Không giúp một cách trung thực. Đây là cách tôi đã giải quyết vấn đề của mình: stackoverflow.com/a/20137542/1455709
Patrick

Có, trường hợp này là chèn một lệnh thuộc tính vào một lệnh khác, không phải là phần tử chèn trong mẫu.
Riceball LEE

Lý do đằng sau làm điều đó bên ngoài mẫu là gì?
Patrick

9
function addAttr(scope, el, attrName, attrValue) {
  el.replaceWith($compile(el.clone().attr(attrName, attrValue))(scope));
}

5

Câu trả lời được chấp nhận bởi Josh David Miller hoạt động rất tốt nếu bạn đang cố gắng tự động thêm một lệnh sử dụng nội tuyến template. Tuy nhiên nếu chỉ thị của bạn tận dụng templateUrlcâu trả lời của anh ta sẽ không hiệu quả. Đây là những gì làm việc cho tôi:

.directive('helperModal', [, "$compile", "$timeout", function ($compile, $timeout) {
    return {
        restrict: 'E',
        replace: true,
        scope: {}, 
        templateUrl: "app/views/modal.html",
        link: function (scope, element, attrs) {
            scope.modalTitle = attrs.modaltitle;
            scope.modalContentDirective = attrs.modalcontentdirective;
        },
        controller: function ($scope, $element, $attrs) {
            if ($attrs.modalcontentdirective != undefined && $attrs.modalcontentdirective != '') {
                var el = $compile($attrs.modalcontentdirective)($scope);
                $timeout(function () {
                    $scope.$digest();
                    $element.find('.modal-body').append(el);
                }, 0);
            }
        }
    }
}]);

5

Josh David Miller là chính xác.

PCoelho, Trong trường hợp bạn đang tự hỏi điều gì $compileđằng sau hậu trường và cách tạo ra HTML từ chỉ thị, vui lòng xem bên dưới

Các $compiledịch vụ biên dịch các đoạn HTML ( "< test text='n' >< / test >") trong đó bao gồm các chỉ thị ( "test" như một yếu tố) và tạo ra một hàm. Hàm này sau đó có thể được thực thi với một phạm vi để lấy "đầu ra HTML từ một lệnh".

var compileFunction = $compile("< test text='n' > < / test >");
var HtmlOutputFromDirective = compileFunction($scope);

Thêm chi tiết với các mẫu mã đầy đủ tại đây: http://www.learn-angularjs-apps-projects.com/AngularJs/dynamical-add-directives-in-angularjs


4

Lấy cảm hứng từ nhiều câu trả lời trước đây, tôi đã đưa ra chỉ thị "stroman" sau đây sẽ tự thay thế bằng bất kỳ chỉ thị nào khác.

app.directive('stroman', function($compile) {
  return {
    link: function(scope, el, attrName) {
      var newElem = angular.element('<div></div>');
      // Copying all of the attributes
      for (let prop in attrName.$attr) {
        newElem.attr(prop, attrName[prop]);
      }
      el.replaceWith($compile(newElem)(scope)); // Replacing
    }
  };
});

Quan trọng: Đăng ký các chỉ thị mà bạn muốn sử dụng restrict: 'C'. Như thế này:

app.directive('my-directive', function() {
  return {
    restrict: 'C',
    template: 'Hi there',
  };
});

Bạn có thể sử dụng như thế này:

<stroman class="my-directive other-class" randomProperty="8"></stroman>

Để có được điều này:

<div class="my-directive other-class" randomProperty="8">Hi there</div>

Protip. Nếu bạn không muốn sử dụng các chỉ thị dựa trên các lớp thì bạn có thể thay đổi '<div></div>'thành thứ bạn thích. Ví dụ, có một thuộc tính cố định có chứa tên của lệnh mong muốn thay vì class.


Vấn đề tương tự tôi đang gặp phải, Bạn có thể giúp tôi ở đây stackoverflow.com/questions/38821980/ không
pandu das

CHÚA ƠI. phải mất 2 ngày để tìm thấy $ compile này ... cảm ơn bạn bè .. nó hoạt động tốt nhất ... AJS bạn rock ....
Srinivasan
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.