为初始编译后创建的元素创建双向绑定

时间:2016-05-26 14:14:52

标签: javascript html angularjs

我创建了一个指令,它根据服务器的json动态创建表单。我尝试将ng-model属性添加到各种输入元素,以便在用户输入并单击提交后我能够使用输入值。似乎添加了ng-model属性,但双向数据绑定不起作用。

编辑:我在链接功能中调用buildForm,如下所示:

function link(scope, elem, attr, ctrl) {
    //asyc request to the server, data here is a json object from the server
    getMovieDataStructure({
        onSuccess: (data) => {
            scope.mdb = data;
            buildForm(scope.mdb, elem);
        },
        onFail: (res) => {
            console.log("ERROR getting it");
        }
    });            
}

以下是该指令中的一些代码:

//mdb is an array of objects describing the form requirments
function buildForm(mdb, formElement) {
    for(var i=0; i < mdb.length; i++) {
        if(mdb[i].type == 'string') {
            if(mdb[i].maxLength && mdb[i].maxLength > 1024) {
                //if maxLength > 1024 put a text area instead
                formElement.append(createTextArea({
                    id: mdb[i].fieldName,
                    placeholder: mdb[i].fieldName
                }));
            } else {
                //add input field to the form
                formElement.append(createTextInput({
                    id: mdb[i].fieldName,
                    placeholder: mdb[i].fieldName
                }));
            }
        } else if(){
            //some more cases
        }

        formElement.append("<br>");
    }
    //...some more code...
}

//one of the functions to create an input element
function createTextInput(data) {
    var elem = angular.element("<input>");
    elem.attr("type", "text");
    elem.attr("id", data.id);
    elem.attr("ng-model", data.id);
    elem.attr("placeholder", data.placeholder);

    return elem;
}

例如,html页面上的输入元素的结果可能如下所示:

<input placeholder="movie_name" ng-model="movie_name" id="movie_name" type="text"> </input>

如果我将相同的标签直接放在html文件中,那么双向绑定效果很好。

这里缺少什么?有没有更好的方法来做到这一点,我只是过于复杂的事情?

4 个答案:

答案 0 :(得分:1)

在您更新表单后的某个地方,您需要调用$ compile,否则angular将不会知道您的更改。见:

https://docs.angularjs.org/api/ng/service/ $编译

答案 1 :(得分:0)

尝试调用buildform方法后,可以调用$ rootScope.apply()。可能发生的事情是,在摘要周期完成后,您正在对DOM进行所有这些更改,而角度将不会知道您的更改,直到下一个周期发生。

所以在你的情况下它将是:         buildForm(scope.mdb,elem);         。的范围$申请();

答案 2 :(得分:0)

事情是需要在你的情况下明确调用digest循环因为angular不知道所做的更改。

使用:

buildForm(scope.mdb, elem); 
scope.$apply();

OR

但使用$ apply有更好的方法:

scope.$apply(buildForm(scope.mdb,elem));

不同之处在于,在第一个版本中,我们正在更新角度上下文之外的值,因此如果抛出错误,Angular将永远不会知道。

答案 3 :(得分:0)

正如wdanda所提到的,由于该指令添加了DOM元素,因此需要在之后编译以让angular知道更改

简短回答是,行buildForm(scope.mdb, elem);已更改为$compile(buildForm(scope.mdb, elem).contents())(scope);,并且'$compile'已添加到指令的依赖项列表中。

长解释:

buildForm(scope.mdb,elem)返回指令的元素(因此在$compile(elem.contents())(scope);之后实际添加buildForm将是等效的),.contents()在角度包装元素上返回所有元素子元素

这意味着$compile(buildForm(scope.mdb, elem).contents())告诉angular编译指令元素的所有子元素,在buildForm添加了一些元素之后(其中一些元素有自己的指令。

.contents()的来电很重要,因为:

  

我们只编译.childNodes,以便我们不会进入无限循环编译自己

(来自https://docs.angularjs.org/api/ng/service/ $ compile)

$compile()函数返回一个链接函数,需要使用要链接的作用域调用。因此,在末尾添加(scope)将调用返回的函数。

编写该代码的更清晰(虽然稍微不那么优雅)的方式是:

var element = buildForm(scope.mdb, elem); //buildForm returns an angular wraped element
var linking = $compile(element); // $compile returns a linking  function
linking(scope); //linking is functions that takes a scope object
                //and needs to be run after compilation