AngularJS создает (и связывает) элементы формы динамически на основе ответа API
Я пытаюсь создать форму участника со всеми предопределенными типами телефонов.
Я делаю два запроса API для членов и типов телефонов. Вот что я имею:
Первый запрос с информацией участника:
$scope.member = {
// $scope.member is returned from an api call
"name": "John Doe",
"phones": [{
"id": "Cell",
"phone": "(651) 111-1899",
"phone_unlisted": false
}, {
"id": "Work",
"phone": "(555) 121-1212",
"phone_unlisted": true
}]
};
Второй запрос на получение типов телефонов:
myApp.controller("phoneTypes", function ($scope) {
// $scope.phoneTypes is returned from an api call
$scope.phoneNames = [{
"id": "Cell"
}, {
"id": "Cell 2"
}, {
"id": "Work"
}, {
"id": "Work 2"
}];
});
Неудачная попытка создать эту форму.
HTML -
<div ng-controller="memberInfo">
<h3>{{member.name}}</h3>
<form role="form" ng-controller="phoneTypes">
<h4>Phones</h4>
<div ng-repeat="phoneName in phoneNames" >
<label>{{phoneName.id}}</label>
//need to bind phone detail from $scope.member if exists otherwise leave blank
<input type="text" ng-model="member.phones.phone" />
unlisted: <input type="checkbox" ng-model="member.phones.phone_unlisted" />
</div>
</form>
</div>
Мне нужно привязать номер телефона каждого соответствующего типа телефона, в противном случае оставьте поле ввода пустым.
Вот http://jsfiddle.net/4nzg4/
Я прошел различные уроки и посты, но я не могу решить это. Я новичок в AngularJS, любая помощь будет оценена.
Спасибо,
2 ответа
Другой подход, который не нарушает двунаправленную привязку сразу, но имеет свои недостатки, заключается в добавлении недостающих типов телефонов в $scope.member
непосредственно. Это немного большая модификация вашего существующего кода.
Во-первых, нам нужно объединить контроллеры и шаблоны в один шаблон:
<div ng-controller="memberInfo">
<h3>{{member.name}}</h3>
<form role="form">
<h4>Phones</h4>
<div ng-repeat="phone in member.phones" >
<label>{{phone.id}}</label>
<input type="text" ng-model="phone.phone" />
unlisted: <input type="checkbox" ng-model="phone.phone_unlisted" /> <br/>
</div>
</form>
</div>
Обратите внимание, что мы сейчас repeat
через member.phones
непосредственно. Но это не имеет все доступные типы телефонов? Исправьте, но мы исправим это сейчас.
var myApp = angular.module("myApp", []);
myApp.controller("memberInfo", function ($scope) {
// $scope.member is returned from an api call
$scope.member = {
"name": "John Doe",
"phones": [{
"id": "Cell",
"phone": "(651) 111-1899",
"phone_unlisted": false
}, {
"id": "Work",
"phone": "(555) 121-1212",
"phone_unlisted": true
}]
};
var phoneNames = [{
"id": "Cell"
}, {
"id": "Cell 2"
}, {
"id": "Work"
}, {
"id": "Work 2"
}];
// make an array of the phone id's in $scope.member.phones
var currently_used_names = $scope.member.phones.map( function( phone ) {
return phone.id;
} ); // returns [ "Cell", "Work" ]
// add any phone names that weren't listed
phoneNames.forEach( function( name ) {
if ( currently_used_names.indexOf( name.id ) === -1 ) {
$scope.member.phones.push( {
"id": name.id,
"phone": "",
"phone_unlisted": false
} );
}
} );
});
В этом случае мы добавляем $scope.member.phones
любые типы телефонов, которые должны быть перечислены с каким-то шаблоном по умолчанию.
Преимущество: сохраняет двунаправленную привязку.
Недостаток: добавляет "неправильные" данные в $scope.member.phones
хотя, вероятно, достаточно легко отфильтровать вручную при необходимости.
Вот скрипка для демонстрации.
Одно из возможных решений - хотя мне это неудобно, но это лучшее - это переназначение phones
массив в phones_object
где ключ является id
телефона. Другими словами, в конце вашего memberInfo
контроллер, вы можете:
$scope.member.phones_object = {};
$scope.member.phones.forEach( function( phone ) {
$scope.member.phones_object[ phone.id ] = phone;
} );
Затем в шаблоне вы используете этот объект для отображения телефонных номеров:
<div ng-repeat="phoneName in phoneNames" >
<label>{{phoneName.id}}</label>
<input type="text" ng-model="member.phones_object[ phoneName.id ].phone" />
unlisted: <input type="checkbox" ng-model="member.phones_object[ phoneName.id ].phone_unlisted" /> <br/>
</div>
Это дает вам то, что я считаю желаемым результатом.
К сожалению, это нарушает двунаправленную привязку Angular, по крайней мере, к оригиналу $scope.members.phones
данные. Вы могли бы использовать $watch
чтобы синхронизировать данные, если вы хотите, или просто синхронизировать их, когда вы будете готовы повторно отправить данные в API, в зависимости от вашего приложения.