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, в зависимости от вашего приложения.

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