AngularJS - 带指令的模块化表单

hgc*_*rpd 9 forms angularjs angularjs-directive

我最初在这里提出了这个问题,但我认为我已经领先于自己并使其变得比实际情况更复杂,所以我在这里重新提出一些更清晰的措辞.

如何使用指令和可重用参数创建可重用的窗体小部件?像这样:

<form>
<special-input label-text="A Special Input" bind-to="data.special"></special-input>
<special-input label-text="Specialer" bind-to="data.moreSpecial"></special-input>
</form>
Run Code Online (Sandbox Code Playgroud)

指令模板似乎不允许在ng-model上进行插值.

此外,您是否可以模块化和参数化表单行为,以便您可以进行标准的POST操作,例如?

我根据我的实验回答了下面的问题,但是我暂时不接受它,因为我对Angular很新,并希望听到其他人的意见.

hgc*_*rpd 15

Angular开箱即用,带有一个改进的标签,在此处记录. 基本上,它围绕表单及其中的所有标记以控制器的形式创建范围.所以你这样做:

<body ng-app="TestApp">
<form ng-controller="FormCtrl" name="testForm">
    <input name="firstInput" ng-model="data.first">
    <input name="secondInput" ng-model="data.second">
    <button ng-click="submit()">Submit</button>
</form>
</body>
Run Code Online (Sandbox Code Playgroud)

JS:

var app = angular.app('TestApp', []);
app.controller('FormCtrl', function($scope) {
    $scope.submit = function() {
        // Form submit logic here
        console.log("Submitting the form");
        console.log($scope);
    }
})
Run Code Online (Sandbox Code Playgroud)

这会为表单创建一个范围,因为表单标记包含ng-controller标记.在范围内,testForm是表单testForm.firstInput的javascript对象,并且是第一个输入的javascript对象.看起来这些对象也有一些可用的验证功能,请参阅此处的文档.

表单上的数据将作为FormCtrl范围内的对象数据提供,其中键为"first"和"second",您可以在控制器中定义处理该方法的方法.

您也可以使用相同的FormCtrl放置多个表单,似乎Angular将为每个表单创建新实例,因此您不必担心表单会污染彼此的数据.

使用指令

现在让我们假设我们有一些在指令中实现的复杂输入或小部件.此示例使用两个选择框来显示州中的所有城市.您必须首先选择一个州,然后它将查询该州的城市并填充第二个选择框.

app.directive('citySelect', function() {
    return {
        replace: true,
        template: '<div><select ng-change="getCities()" ng-options="s.name for s in states"></select>' +
                  '<select ng-model="data.selectedCity" ng-options="c.name for c in cities"></select>',
        controller: function($scope) {
            // Omitting the logic for getCities(), but it'd go here
        }
    };
})
Run Code Online (Sandbox Code Playgroud)

然后你可以将它粘贴到表单标签中,它会起作用.因为该指令没有定义范围,所以它只会附加到FormCtrl的范围.

<body ng-app="TestApp">
<form ng-controller="FormCtrl" name="testForm">
    <input name="firstInput" ng-model="data.first">
    <input name="secondInput" ng-model="data.second">
    <div city-select></div>
    <button ng-click="submit()">Submit</button>
</form>
</body>
Run Code Online (Sandbox Code Playgroud)

对指令进行参数化

编辑: 显然这确实有效:

scope: {someParameter: "="},
template: '<div><select ng-model="someParameter"></select></div>'
Run Code Online (Sandbox Code Playgroud)

你只是在没有curlies的情况下完成它,它会绑定.我的猜测是父作用域绑定到子作用域中的someParameter,然后select绑定到子作用域中的somParameter.

因此,下面关于在链接函数中手动编译的所有这些都不是必需的.

=====

但问题是我的citySelect指令有一个硬编码的ng-model绑定,所以如果我创建了某种通用的widget,我就不能在表单中使用多个.不幸的是,这似乎不起作用:

scope: {someParameter: "="},
template: '<div><select ng-model="{{ someParameter }}"></select></div>'
Run Code Online (Sandbox Code Playgroud)

我实现这一点的唯一方法是在链接函数中手动构建DOM元素,但我不确定这是否可取.我很感激任何人对此实施的评论:

<body ng-app="TestApp">
<form ng-controller="FormCtrl" name="testForm">
    <input name="firstInput" ng-model="data.first">
    <input name="secondInput" ng-model="data.second">
    <div city-select bind-to="data.homeCity"></div>
    <div city-select bind-to="data.workCity"></div>
    <button ng-click="submit()">Submit</button>
</form>
</body>

app.directive('citySelect', function($compile) {
    return {
        replace: true,
        template: '<div></div>',
        controller: function($scope) {
            // Omitting the logic for getCities(), but it'd go here
        }
        link: function(scope, iElem, iAttrs) {
            var html = '<div><select ng-bind="' + iAttrs['bindTo'] + '"></div>';
            iElem.replaceWith($compile(html)(scope));
        }
    };
})
Run Code Online (Sandbox Code Playgroud)

混合表单上的参数

由于为每个表单创建了FormCtrl的单独实例,因此可以重用FormCtrl中的许多功能.但您也可以在表单标记上使用其他指令来添加参数或拆分功能.例如:

<form ng-controller="FormCtrl" name="testForm" post-form post-path="/path/to/resource/">

app.directive('postForm', function() {
    return {
        controller: function($scope) {
            $scope.post = function() {
                // Some generic POST behavior
            };
        },
        link: function(scope, iElem, iAttr) {
            scope.postPath = iAttr['postPath'];
        },
    };
});
Run Code Online (Sandbox Code Playgroud)

然后,表单的作用域将组合FormCtrl和postForm的作用域,以便可以访问所有内容.在我的实验中,似乎FormCtrl优先,所以如果在FormCtrl和postForm中都定义了类似$ scope.submit()的东西,FormCtrl将优先(我认为),也许这是异步加载的竞争条件,我不知道.

我认为你也可以scope:true在mixin指令(postForm)上使用,或者更安全地使用ng-controller,而不是使用ng-controller scope: {}.