AngularJS - Модульные формы с директивами

Первоначально я задавал этот вопрос здесь, но я думаю, что я опередил себя и сделал его более сложным, чем он есть на самом деле, поэтому я переспрашиваю его здесь с более четкой формулировкой

Как вы создаете многократно используемые виджеты форм с директивами и повторно используемыми параметрами? Как это:

<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-модели.

Кроме того, можете ли вы, например, модулировать и параметризировать поведение формы, чтобы иметь стандартные действия POST?

Я отвечаю на вопрос ниже, основываясь на моих экспериментах, но я не буду принимать его некоторое время, так как я очень новичок в Angular и хотел бы услышать от других.

1 ответ

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

Это создает область видимости для формы, поскольку тег form содержит тег 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 в дочерней области, и затем выбор связывается с somParameter в дочерней области.

Так что все это ниже о ручной компиляции в функции ссылки не является обязательным.

=====

Но проблема в том, что моя директива citySelect имеет жестко привязанную привязку ng-модели, поэтому, если я создал какой-то общий виджет, я не смог бы использовать более одного из них в форме. К сожалению, это не похоже на работу:

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 имеет приоритет, поэтому, если что-то вроде $scope.submit() определено в FormCtrl и postForm, FormCtrl будет иметь приоритет (я думаю), возможно, это условие гонки из-за асинхронной загрузки, я не знаю

Вместо использования ng-контроллера, я думаю, вы также можете использовать scope:true по директиве mixin (postForm) или, возможно, более безопасно, scope: {},

Другие вопросы по тегам