Используйте ng-модель с примитивным значением внутри ng-transclude

В устаревшем проекте я хочу создать новую директиву, использующую transclude.

Урезанная версия кода директивы:

app.directive('controlWrap', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: { label: "@" },
        templateUrl: "control-wrap-template.html"
    }
})

А шаблон такой:

<div>
    <label>{{label}}</label>
    <div>
        <ng-transclude></ng-transclude>
    </div>
</div>

Эта директива используется так

<control-wrap label="Just a example">
    <input type="text" ng-model="input" />
</control-wrap>
Test: {{input}}

Я знаю, что обходной путь заключается в использовании объекта в области вместо примитивного значения (ng-модель внутри ng-transclude). Но это не вариант для меня. Это уродливый, плохо закодированный устаревший код, который опирается на эти атрибуты непосредственно в области видимости.

Есть ли что-то, что я могу сделать в директиве, чтобы html работал без изменений?


person Fernando    schedule 17.05.2015    source источник
comment
Устаревший код не должен иметь никакого отношения, почему вы не можете использовать объект? Или вам действительно нужно изолировать область действия в этой директиве?   -  person charlietfl    schedule 18.05.2015
comment
Это имеет значение, потому что это ужасное количество кода, которое нужно изменить. Я хотел бы использовать изолированную область, но я пытаюсь переписать директиву по-другому. (имейте в виду, что я опубликовал упрощение моей проблемы, а не фактическую директиву)   -  person Fernando    schedule 18.05.2015
comment
@Fernando Фернандо. Я не особенно поддерживаю здесь свое хакерское решение, но если вы не в себе и вам нужно что-то, которое работает и соответствует вашим требованиям (т.е. без изменений основного HTML), то взгляните на - plnkr.co/edit/u4vMParXvdp26LDgFGxa?p=preview   -  person miqh    schedule 18.05.2015
comment
Мне нравится, что ! Я знаю, что это хакерское решение, но решите мою проблему. Можете ли вы опубликовать свой комментарий в качестве ответа?   -  person Fernando    schedule 18.05.2015


Ответы (2)


Вы можете вручную включить (вместо использования ng-transclude) и применить любую область (в вашем случае scope.$parent), которая вам нужна, к включенному контенту:

transclude: true,
scope: { label: "@" },
template: '<div>\
             <label>{{label}}</label>\
             <placeholder></placeholder>\
           </div>',
link: function(scope, element, attrs, ctrls, transclude){
   transclude(scope.$parent, function(clone){
      element.find("placeholder").replaceWith(clone);
   });
}

Демо

person New Dev    schedule 18.05.2015
comment
Я не знаю, хорошо ли это, мне не хватает знаний, но я выбираю это решение, потому что это идея, которую я использую прямо сейчас. Но ответ @gyantasaurus очень хорош, и комментарий от miqid тоже имеет свои достоинства. - person Fernando; 19.05.2015

Самое чистое решение — выполнить некоторый рефакторинг и передать объект вместо примитивного значения, но если по какой-то причине вы не можете этого сделать, у вас есть варианты.

Однако я бы не рекомендовал ни один из этих вариантов

1) Привязать input из родительской области, что предотвращает создание нового значения в дочерней области при записи, но имейте в виду, что доступ к родительской области вредит повторному использованию вашей директивы. Угловой 1.2:

<input type="text" ng-model="$parent.input" />

Угловой 1.3:

<input type="text" ng-model="$parent.$parent.input" />

(Разница в том, что родителем включенной области является директивная область из 1.3)

2) Создайте какой-то объект-оболочку и передайте его вместо примитивного значения

$scope.inputWrapper = {};
Object.defineProperty($scope.inputWrapper, 'input', {
    get: function() { return $scope.input },
    set: function(newValue) { $scope.input = newValue; }
})

и передать это директиве. Но опять же, вместо этого я бы сделал некоторый рефакторинг.

person gyantasaurus    schedule 18.05.2015