10

我最初在这里问了这个问题,但我认为我走得太远了,让它比实际情况更复杂,所以我在这里用更清晰的措辞重新问这个问题。

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

<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>

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

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

我已经根据我的实验回答了下面的问题,但我暂时不会接受它,因为我对 Angular 很陌生,想听听其他人的意见。

4

1 回答 1

14

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>

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);
    }
})

这为表单创建了一个范围,因为表单标签包含 ng-controller 标签。在范围内,testForm是表单的 javascript 对象,并且testForm.firstInput是第一个输入的 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
        }
    };
})

然后你可以把它粘贴到表单标签中,它就可以工作了。因为该指令没有定义范围,它只会附加到 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>

参数化指令

编辑: 所以显然这确实有效:

scope: {someParameter: "="},
template: '<div><select ng-model="someParameter"></select></div>'

您只需在没有卷曲的情况下执行此操作,它就会绑定。我的猜测是父作用域绑定到子作用域中的 someParameter,然后 select 绑定到子作用域中的 somParameter。

所以下面关于在链接函数中手动编译的所有这些都是不必要的。

=====

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

scope: {someParameter: "="},
template: '<div><select ng-model="{{ someParameter }}"></select></div>'

我让它工作的唯一方法是在链接函数中手动构建 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));
        }
    };
})

在表单上混合参数

由于为每个表单创建了单独的 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'];
        },
    };
});

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

除了使用 ng-controller,我认为您还可以scope:true在 mixin 指令 (postForm) 上使用,或者更安全地使用scope: {}.

于 2013-07-08T14:54:04.193 回答