Директива AngularJS для анализа и замены содержимого пользовательских элементов

Я хотел бы создать простую директиву markdown, которая принимает некоторый контент внутри элемента, анализирует его и заменяет его html.

Итак, это:

<markdown>#Heading</markdown>

или (где $scope.heading = '#Heading';)

<markdown>{{heading}}</markdown>

Становится следующим:

<h1>Heading</h1>

Моя директива пока (очевидно, не завершена!):

.directive('markdown', function () {
    return {
        restrict: 'E',
        replace: true,
        link: function ($scope, $element, $attrs) {
            // Grab contents
                var contents = /* How do I do this? */

                var newContents = Markdowner.transform(contents);

                // Replace <markdown> element with newContents
                /* How do I do this? */
        }
    }
})

Я не уверен, как захватить содержимое директивы? Мне нужно скомпилировать его?!

Анализ разметки - это просто пример

Ответ 2

ngTransclude специально разработан для этого.

myModule.directive('heading', function() {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: true,
        template: '<h1 ng-transclude></h1>'
    };
}

Затем используйте его следующим образом:

<heading><span>{{foo}}</span></heading>

Здесь работает fiddle (angular 1.2.7).

Кроме того, я предполагаю, что вам нужна какая-то интеграция с уценкой. Ниже приведена версия, использующая transclude, чтобы вы получили контейнер div.

Этот пропускает все поведение transclude, и я предполагаю, что это ближе к тому, что вы после.

Ответ 3

Вы можете получить и установить скомпилированное содержимое элемента в функции ссылок, используя:

element.html() //get
element.html("blah") //set

Вот пример, основанный на примере Sergiu ниже, который обрабатывает привязки, содержащиеся в html, используя scope.$eval(), перед вызовом конвертора меток:

http://jsfiddle.net/edeustace/4rE85/1/

angular.module('transclude', [])
 .directive('markdown', function() {

  var regex = /\{\{(.*?)\}\}/;

  var converter = new Showdown.converter();

  return {
    restrict: 'E',
    replace: true,
    scope: true,
    link: function (scope, element) {

      var processTemplate = function(text){
        var results = text.match(regex);
        if(!results){
          return text;
        } else {
          var value = scope.$eval(results[1]);
          var replaceKey = new RegExp("{{" + results[1] + "}}","g");
            text = text.replace(replaceKey, value);
            return processTemplate(text);
        }
     };
     var text = element.text();
     var processed = processTemplate(text);
     var markdownText = converter.makeHtml(processed);
     element.html(markdownText);
    }

  };
});

который будет работать с:

<markdown>
# Bar {{foo}} {{foo}}
# {{bing}}
</markdown>

Или вы можете привязать его к атрибуту, который затем можно использовать в своей директиве:

app.directive('markdownWithBinding', function () {

  var converter = new Showdown.converter();

  return {
    restrict: 'E',
    scope: {
      'md' : '@'
    },
    link: function  ($scope, $element, $attrs) {

      $scope.$watch('md', function(newMd){

        var markdownText = converter.makeHtml(newMd);
        element.html(markdownText);

      });
    }
  }
});

Используется так:

<markdown-with-binding md="Hello {{name}}"></markdown-with-binding> 
<!-- outputs Hello World!!! -->

Старый ответ

Это произойдет в link(), который предназначен для связывания области с элементом. Для структурных изменений, когда не требуется никаких областей, вам может быть лучше сделать изменения в функции компиляции:

app.directive('markdown', function () {

var link = function ($scope, $element, $attrs) {};
return {
    restrict: 'E',
    replace: true,
  compile: function($element, $attrs, $transclude){

    if($element.html() == "#Hello"){
      $element.html("<h1>Hello</h1>");
    }
    return link;
  },
}

});

Здесь отличный учебник по компонентам: http://www.youtube.com/watch?v=A6wq16Ow5Ec

Ответ 4

В вашей функции связи AngularJS уже проанализировал ваш HTML-код и заменил содержимое вашим шаблоном (который в вашем случае отсутствует, так как вы "замените" на true).

Вы можете захватить внутреннее содержимое html из элемента $, который является элементом jQuery (jQLite).

var contents = $element.innerHTML;