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: {}
,