2015-04-03 5 views
2

Я реализую пользовательский входной виджет. Реальный код является более сложным, но в целом это выглядит следующим образом:Должен ли я использовать область выделения в этом случае?

app.directive('inputWidget', function() { 
    return { 
     replace:true, 
     restrict: 'E', 
     templateUrl:"inputWidget.html", 
     compile: function (tElement, tAttributes){ 
      //flow the bindings from the parent. 
      //I can do it dynamically, this is just a demo for the idea 
      tElement.find("input").attr("placeholder", tAttributes.placeholder); 
      tElement.find("input").attr("ng-model", tElement.attr("ng-model")); 
     } 
    }; 
}); 

inputWidget.html:

<div> 
    <input /> 
    <span> 
    </span> 
</div> 

Чтобы использовать его:

<input-widget placeholder="{{name}}" ng-model="someProperty"></input-widget> 

Заполнитель правильно отображаются выше потому что он использует ту же область действия родителя: http://plnkr.co/edit/uhUEGBUCB8BcwxqvKRI9?p=preview

Я удивлен г, если я должен использовать изолят сферу, как это:

app.directive('inputWidget', function() { 
     return { 
      replace:true, 
      restrict: 'E', 
      templateUrl:"inputWidget.html", 
      scope : { 
       placeholder: "@" 
       //more properties for ng-model,... 
      } 
     }; 
}); 

С этим, директива не разделяет ту же область с родителем, который мог бы быть хороший дизайн. Но проблема в том, что определение изоляционной области быстро станет беспорядочным, поскольку мы помещаем в него DOM-связанные свойства (заполнитель, тип, обязательный, ...) и каждый раз, когда нам нужно применять новую директиву (выборочная проверка на вход-виджет), нам нужно определить свойство в области изоляции, чтобы действовать как средний человек.

Мне интересно, нужно ли всегда определять область изоляции для директивных компонентов.

В этом случае, у меня есть 3 варианта:

  • Используйте ту же область, что и родитель.
  • Используйте изоляционный объем, как я сказал выше.
  • Используйте изолированную область действия, но не привязывайте к ней свойства, связанные с DOM, так или иначе передайте свойства DOM из родителя напрямую. Я не уверен, что это хорошая идея, и я не знаю, как это сделать.

Прошу совета, спасибо.

+0

Итак, является требование, чтобы произвольные атрибуты будут применяться к 'input' элемента в шаблоне? –

+0

@New Dev: большинство из них делают. Эта директива состоит лишь в том, чтобы добавить еще несколько визуальных эффектов, основная функциональность ввода не должна сильно меняться. –

+0

вы можете использовать 'scope: true' - он наследует внешнюю область –

ответ

1

Это не очень тривиальная проблема. Это связано с тем, что у шаблонов могут быть произвольные директивы, предположительно предназначенные для <input>, и правильное решение должно гарантировать, что: 1) эти директивы собирают и связывают только один раз и 2) компилируются против фактического <input> - не <input-widget>.

По этой причине я предлагаю использовать фактический <input> элемент, и добавить inputWidget директиву как атрибут - эта директива будет применяться шаблоном, в то время как фактический <input> элемент будет принимать другие директивы (как ng-model, ng-required, пользовательских валидаторов, и т. д.), которые могли бы работать на нем.

<input input-widget 
     ng-model="someProp" placeholder="{{placeholder}}" 
     ng-required="isRequired" 
     p1="{{name}}" p2="name"> 

и inputWidget будет использовать две компиляции проходов (по образцу ngInclude):

app.directive("inputWidget", function($templateRequest) { 
    return { 
    priority: 400, 
    terminal: true, 
    transclude: "element", 
    controller: angular.noop, 
    link: function(scope, element, attrs, ctrl, transclude) { 
     $templateRequest("inputWidget.template.html").then(function(templateHtml) { 
     ctrl.template = templateHtml; 
     transclude(scope, function(clone) { 
      element.after(clone); 
     }); 
     }); 
    } 
    }; 
}); 

app.directive("inputWidget", function($compile) { 
    return { 
    priority: -400, 
    require: "inputWidget", 
    scope: { 
     p1: "@", // variables used by the directive itself 
     p2: "=?" // for example, to augment the template 
    }, 
    link: function(scope, element, attrs, ctrl, transclude) { 
     var templateEl = angular.element(ctrl.template); 
     element.after(templateEl); 
     $compile(templateEl)(scope); 
     templateEl.find("placeholder").replaceWith(element); 
    } 
    }; 
}); 

Шаблон (inputWidget.template.html) имеет <placeholder> элемент для обозначения, где поместить оригинальный <input> элемент:

<div> 
    <pre>p1: {{p1}}</pre> 
    <div> 
    <placeholder></placeholder> 
    </div> 
    <pre>p2: {{p2}}</pre> 
</div> 

Demo

(EDIT) Почему 2 компиляции проходит:

Решением выше, является «обходным путем», что позволяет избежать ошибок в угловому, который метание с интерполировать значения создаются на комментарий элемент, который является то, что остается, когда transclude: element используется. Это было исправлено в v1.4.0-beta.6, и с исправлением, решение может быть упрощено до:

app.directive("inputWidget", function($compile, $templateRequest) { 
    return { 
    priority: 50, // has to be lower than 100 to get interpolated values 
    transclude: "element", 
    scope: { 
     p1: "@", // variables used by the directive itself 
     p2: "=" // for example, to augment the template 
    }, 
    link: function(scope, element, attrs, ctrl, transclude) { 
     var dirScope = scope, 
      outerScope = scope.$parent; 

     $templateRequest("inputWidget.template.html").then(function(templateHtml) { 
     transclude(outerScope, function(clone) { 
      var templateClone = $compile(templateHtml)(dirScope); 
      templateClone.find("placeholder").replaceWith(clone); 
      element.after(templateClone); 
     }); 
     }); 
    } 
    }; 
}); 

Demo 2

+0

очень хорошая идея использования transclude. Но почему нам нужно 2 'app.directive (« inputWidget »'?? Можно ли использовать один? –

+0

@KhanhTO, почему-то интерполяция выражения '{{p1}}' не работала, когда 'terminal: true' и' приоритет' превышает 100. http://plnkr.co/edit/pEkRRsha1UP8r6XsQM9R?p = предварительный просмотр –

+0

@KhanhTO, v1.4.0-beta.6 исправил проблему, которая предотвратила более простой подход. Я обновил ответ –

2

Если конфигурация ввода-виджет является сложной, я хотел бы использовать атрибут, опцию, а также изолированная сферу, чтобы сделать атрибут явной и обязательным:

<input-widget options="{ placeholder: name, max-length: 5, etc }" 
    ng-model="name"></input-widget> 

Там нет необходимости течь любой DOM атрибутов, если у вас есть модель вариантов, и ngModel:

app.directive('inputWidget', function() { 
    return { 
     replace:true, 
     restrict: 'E', 
     templateUrl:"inputWidget.html", 
     scope: { options:'=', ngModel: '='} 

    }; 
}); 

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

<div> 
    <input placeholder="{{options.placeholder}}" ng-model="ngModel"/> 
    <span> 
    {{options}} 
    </span> 
</div> 

Demo

Лично при разработке для повторного использования, я предпочитаю использовать атрибуты в качестве средства конфигурирования директивы и изолированной области, чтобы сделать его более модульным и читаемым. Он больше похож на компонент и обычно не нуждается в внешнем контексте.

Однако есть моменты, когда я нахожу директивы с дочерними/унаследованными областями полезными. В таких случаях я обычно «требую» родительскую директиву для предоставления контекста.Пара директив работает вместе, чтобы меньше атрибутов приходилось передавать дочерней директиве.

+0

спасибо за предложение, +1. Я жду, чтобы увидеть, есть ли лучшее предложение –

Смежные вопросы