Final Fiddle
Angularjs Цепные Директивы Замена элементов
Я начал с целью разработать общую директиву для визуализации формы элементов для Activiti задач двигателей, использующих Angularjs. Для чего я разработал директиву (скажем, dir1), которая на основе определенных свойств элемента формы предоставила соответствующий тип html-элемента (ввод (текст, флажок), выбор или интервал), заменяющий элемент dir1.
Контроллер, который собирает Activiti форму имитируется следующим кодом
функция MyCtrlr ($ охват) {
$scope.v = [{value: 'init0'},
{value: 'init1'},
{value: 'init2'},
{value: 'init3'}
];
$scope.formVals = {
vals: [{
id: 'one',
type: 'string',
value: 'xyz'
}, {
id: 'two',
type: 'enum',
value: '2',
writable:true,
enumValues: [{
'id': 1,
'name': 'ek'
}, {
'id': 2,
'name': 'don'
}]
}, {
id: 'three',
type: 'enum',
value: 'abc',
writable:true,
enumValues: [{
'id': 3,
'name': 'tin'
}, {
'id': 4,
'name': 'chaar'
}]
}, {
id: 'four',
type: 'enum',
value: 'abc',
writable:true,
enumValues: [{
'id': 5,
'name': 'paach'
}, {
'id': 6,
'name': 'sahaa'
}]
},
{id:'five',
type:'string',
value:'test',
writable:true
}
]
};
//$scope.formVals.vals[0].varRef = $scope.v[0];
//$scope.formVals.vals[1].varRef = $scope.v[1];
$scope.formVals.vals[2].varRef = $scope.v[2];
$scope.formVals.vals[3].varRef = $scope.v[3];
$scope.verify = function() {
alert($scope.v[0].value + '...' + $scope.v[1].value + '...' + $scope.v[2].value + '...' + $scope.v[3].value);
};
}
и директива dir1 следующим
приложение ,директива ('dir1', функция ($ компиляции) {
var getTemplate = function(fld, fvarnm, debug) {
value = ' value="' + fld.value + '"';
nm = ' name="' + fld.id + '"';
ngmodel = ' ng-model="' + fvarnm + '.varRef.value"';
disabled = fld.writable?'':' disabled=disabled';
switch(fld.type) {
case 'activitiUser':
case 'enum':
template = '<select '
+ nm + disabled
+ (fld.varRef != null?ngmodel:'');
template += '<option></option>';
for (e in fld.enumValues) {
selected = '';
ev = fld.enumValues[e];
if ((fld.varRef == null && (fld.value == ev.id)) || (fld.varRef != null) && (fld.varRef.value == ev.id))
selected = ' SELECTED ';
template += '<option value="' + ev.id + '"' + selected + '>' + ev.name + '</option>';
}
template += '</select>';
break;
case 'boolean':
template = '<input type="checkbox"'
+ nm + disabled
+ (fld.varRef != null?ngmodel:value)
+ (fld.value?' CHECKED':'')
+ '></input>';
break;
default:
template = '<input type="text"'
+ nm + disabled
+ (fld.varRef != null?ngmodel:value)
+ ' value-format="' + fld.type + ' '
+ fld.datePattern + '"'
+ '></input>';
}
if (fld.varRef != null && typeof(debug) != 'undefined' && debug.toLowerCase() == 'true') {
template = '<div>' + template
+ '<span ng-bind="' + fvarnm
+ '.varRef.value"></span>' + '</div>';
}
return template;
};
return {
restrict: 'E',
replace: true,
scope : {
field : '='
},
link : function(scope, element, attrs) {
html = getTemplate(scope.field, attrs.field, attrs.debug);
element.replaceWith($compile(html)(scope.$parent));
}
};
});
Однако, когда появились нюансы приложения поверх Activiti, я принял решение о том, что я хочу дать разработчику возможность пользователю dir1 для его общих требований и позволить ему разработать свою собственную директиву, прикованную к dir1, для обработки этих нюансы. О нюансах - на основе свойств разработчика приложения элемента элемента формы либо будет использоваться общий рендеринг, предоставляемый dir1, либо заменить элемент dir2 соответствующим элементом html.
Я добавил dir2 следующим образом -
app.directive ('dir2', функция ($ компиляции) {
var getTemplate2 = function(scope, el, attrs) {
html2 = "<dir1 field='" + attrs.field + "'></dir1>";
if (scope.field.id == 'five') {
html2 = '<span style="font-weight:bold" ';
if (typeof(scope.field.varRef) != 'undefined' && scope.field.varRef) {
html2 += ' ng-bind="f.varRef.value" ';
} else {
html2 += ' ng-bind="f.value" ';
}
html2 += '></span> ';
}
return html2;
};
return {
restrict: 'E',
replace : true,
scope : {
field : '='
},
link: function (scope, el, attrs) {
var html2 = getTemplate2(scope, el, attrs);
el.replaceWith($compile(html2)(scope.$parent));
}
};
});
Однако я начал получать нулевую родительскую ошибку в replaceWith call in dir1. После много дезориентированного мышления и консольного ведения журнала я понял, что момент html2 собирался в команде el.replaceWith ($ compile (html2) (scope. $ Parent)), функция dir1 link запускалась всякий раз, когда html2 был элементом dir1. В этот момент элемент dir1 не имел никакого родительскогоNode. Поэтому я придумал следующее соглашение. В функции gettemplate2 значение html2 по умолчанию стало html2 = "", т. Е. Передало родительский атрибут. В функции dir1 link я сделал следующие изменения: html = getTemplate (scope.field, attrs.field, attrs.debug); scope.dir1el = $ compile (html) (scope); if (typeof (attrs.parent) == 'undefined') { element.replaceWith (scope.dir1el); } , предотвращая тем самым замену в каталоге dir1. Дополняет друг друг изменений в dir2 были
var html2 = getTemplate2(scope, el, attrs);
if (html2 == null) {
$compile("<dir1 parent='true' field='" + attrs.field + "'></dir1>")(scope.$parent);
ne = scope.$$nextSibling.dir1el;
} else {
ne = $compile(html2)(scope.$parent);
}
el.replaceWith(ne);
С dir1 и dir2 являются двойниками директивы, я должны были получить доступ к dir1 сфере, используя $$ NextSibling. Таким образом, я могу заменить элемент в dir2 на один, сгенерированный dir1 или dir2, если это необходимо.
Я также разработал альтернативное решение, используя директиву атрибута dir3, где dir3 станет атрибутом dir1. Здесь область dir1 становится родительской областью dir3. И сделанный на заказ элемент в dir3 заменяет элемент, заменяющий элемент, созданный dir1. Таким образом, это решение включает двойную замену DOM.
Когда я пытаюсь использовать ваш плункер, он не работает, входной текст имеет [объект Object] в нем, и двусторонняя привязка данных нарушена. – mortalapeman
Кроме того, обе директивы все еще используют фазу связи. Когда вы возвращаете функцию из функции компиляции, это функция postLink и будет выполняться во время фазы связывания. – mortalapeman
Hummm ... Я заметил это, но я не хотел менять код! Я думал, что вы действительно хотели, чтобы объект [object Object] во входном tex :) Это быстрое решение. просто добавьте '.val' к переменной. здесь это обновленный plunk (http://plnkr.co/edit/GrOPkNaxOxcXFDZfDwWh). Двухстороннее связывание данных работает должным образом. –