我的目标 - 指令dir2替换为指令dir1,而指令dir1又替换为输入。
但是在输入dir1替换期间,我在replaceWith函数中获取parent是null异常。
var app = angular.module("myapp",[]);
function MyCtrlr($scope){
$scope.vars = {val:"xyz"};
}
app.directive("dir2", function($compile){
return {
restrict : 'E',
replace : true,
compile :function(el, attrs) {
var newhtml = '<dir1 field="' + attrs.field + '" />';
return function(scope, el, attrs) {
console.log('dir2 parent = ' + el.parent());
el.replaceWith($compile(newhtml)(scope));
}
}
}
});
app.directive("dir1", function($compile){
return {
restrict : 'E',
replace : true,
compile :function(el, attrs) {
return function(scope, el, attrs) {
console.log('dir1 parent = ' + el.parent());
console.log(scope.field);
el.replaceWith($compile('<input type="text" ng-model="' + attrs.field + '.val" />')(scope));
}
}
}
});
答案 0 :(得分:1)
基本上您收到错误消息,因为编译过程分两个阶段进行:compile
和link
。
由于您的指令是在同一时间编译的(第1阶段),当dir2
完成编译时,dir1
的DOM元素尚未准备好进行操作。
所以我改变dir1
以使用流程的链接阶段(第2阶段)。
像这样dir2
有机会完成并创建dir1
使用的DOM元素(模板)
http://plnkr.co/edit/GrOPkNaxOxcXFDZfDwWh
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script>
var app = angular.module("myApp",[]);
function MyCtrlr($scope){
$scope.vars = {val:"xyz"};
}
app.directive("dir2", function($compile){
return {
restrict : 'E',
replace : true,
compile :function(el, attrs) {
var newhtml = '<dir1 field="' + attrs.field + '" />';
return function(scope, el, attrs) {
console.log('dir2 parent = ' + el.parent());
el.replaceWith($compile(newhtml)(scope));
}
}
}
});
app.directive("dir1", function($compile){
return {
restrict : 'E',
replace : true,
template: '<input type="text" ng-model="field" />',
scope: {
field: '='
},
link: function(scope, el, attrs) {
console.log('dir1 parent = ' + el.parent());
console.log(scope.field);
}
}
});
</script>
</head>
<body>
<div ng-app="myapp">
Testing
<div ng-controller = "MyCtrlr">
<span ng-bind="vars.val"></span>
<dir2 field="vars"></dir2>
</div>
</div>
</body>
</html>
答案 1 :(得分:0)
以下是如何完成您想要做的事情:
Wokring plunker
var app = angular.module('plunker', []);
function MyCtrlr($scope){
$scope.vars = {val:"xyz"};
}
app.directive("dir2", function($compile){
return {
restrict : 'E',
replace : true,
template: '<dir1></dir1>',
link: function(scope, el, attrs) {
}
};
});
app.directive("dir1", function($compile){
return {
restrict : 'E',
scope: {
field: '='
},
link: function(scope, el, attrs) {
scope.model = scope.field;
el.replaceWith($compile('<input type="text" ng-model="model.val" />')(scope));
}
};
});
这保留了双向数据绑定,但其使用相当有限。我假设你的用例是你问题的简化,否则一个不同的方法可能会更简单。
我仍然在研究你的小提琴出了什么问题的细节,当我想出来的时候会发布一个编辑。
答案 2 :(得分:0)
Angularjs链式指令取代元素
我开始的目标是开发一个泛型指令,使用Angularjs为Activiti引擎任务渲染表单元素。 为此,我开发了一个指令(比如dir1),它基于表单元素的某些属性,将呈现适当的类型html元素(输入(文本,复选框),选择或跨度)替换dir1元素。
收集Activiti表单的控制器由以下代码模拟
函数MyCtrlr($ scope){
$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如下
app.directive('dir1',function($ compile){
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提供的泛型渲染,也可以使用适当的html元素替换dir2元素。
我添加dir2如下 -
app.directive('dir2',function($ compile){
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));
}
};
});
但是我在dir1中的replaceWith调用中开始得到null父错误。在经过多次迷失方向思考和控制台日志记录之后,我意识到当html2被编译为el.replaceWith($ compile(html2)(范围。$ parent))语句时,只要html2是dir1元素,dir1链接函数就会触发。此时dir1元素没有任何parentNode。 因此我想出了以下安排。 在gettemplate2函数中,html2默认值变为html2 =“”,即传递父属性。 在dir1链接功能中,我做了以下更改 html = getTemplate(scope.field,attrs.field,attrs.debug); scope.dir1el = $ compile(html)(范围); 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是兄弟指令,我必须使用$$ nextSibling访问dir1范围。因此,允许我将dir2中的元素替换为dir1或dir2生成的元素。
我还使用属性指令dir3开发了一个替代解决方案,其中dir3将成为dir1的属性。这里dir1范围成为dir3的父范围。 dir3中的定制元素替换了dir1创建的元素替换元素。因此,该解决方案涉及双DOM替换。