Search code examples
javascriptangularjsng-options

Creating dynamic dropdown list in AngularJS from two or more sources


I have a nested object like this:

ObjectA-ObjectB-AddressObject-address1
ObjectA-ObjectB-ObjectC-AddressObject-address2

I would like to create dropdown list to a form so that there is always ObjectC's address as a default if there is one. I am totally newbie in AngularJS, so what is the correct way to that? Should I create some kind of option list in my controller or is there any more correct way to that? I tried to search answers to that from Stack, but there is plenty of different solutions. I think that I can't use ngOptions because options are coming from two different source, correct?

<input type="text" ng-model="rma.customerCustomerId.addressAddressId.address"  placeholder="Address"> 

<input type="text" ng-model="rma.retailerRetailerId.addressAddressId.address" placeholder="Address">

I just tried to get values in my controller:

console.log(rma.customerCustomerId.addressAddressId.address);
console.log(rma.retailerRetailerId.addressAddressId.address);

enter image description here


Solution

  • Agreed with one of the comment in the question. This doesn't feel like angular problem. You should just transform your domain model into a more appropriate view model. In the simplest example, probably something like this.

    If your model looks like this:

        self.foo = {
            bar : {
                addr : { value : 'Bar.Address' },
                baz : {
                    addr : { value : 'Baz.Address' }
                }
            }
        };
    

    Then, you can flatten this using something like:

           var result = [];
            if (self.foo.bar.baz) result.push(self.foo.bar.baz.addr.value);
            result.push(self.foo.bar.addr.value);
    
            self.addresses = result;
    

    And then just bind the selected address to the first element in the resulting array:

    self.selectedAddress = self.addresses[0];
    

    Putting it together, you get something like the snippet below.

    (function(undefined) {
        'use strict';
        
        angular.module('myApp',[]);
        
        angular.module('myApp')
            .controller('myCtrl', myCtrl);
        
        myCtrl.$inject = ['$log', '$scope'];
        function myCtrl($log, $scope) {
            var self = this;
            
            self.foo = {
                bar : {
                    addr : { value : 'Bar.Address' },       //remove baz element below to simulate that it's not there.
                    baz : {
                        addr : { value : 'Baz.Address' }    //This will be preferred if exists...
                    }
                }
            };
            
            self.selectedAddress = undefined;
            self.addresses = [];
          
            $scope.$watch(function() { return self.foo.bar.baz.addr.value; }, function(newVal, oldVal) {
              bindAddresses();
            });
    
            activate();
    
            function activate() {
                bindAddresses();
            }
            
            function bindAddresses() {
                var result = [];
                if (self.foo.bar.baz && self.foo.bar.baz.addr && self.foo.bar.baz.addr.value) {
                  result.push(self.foo.bar.baz.addr.value);
                }
                result.push(self.foo.bar.addr.value);
                
                self.addresses = result;
                self.selectedAddress = self.addresses[0];
            }
        }
    }());
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app='myApp'>
        <form ng-controller='myCtrl as vm'>
          
            <p>
            Address 1
            <input ng-model="vm.foo.bar.addr.value" />
            </p>
          
          
            <p>
            Address 2
            <input ng-model="vm.foo.bar.baz.addr.value" />
            </p>
          
          
            <select ng-model='vm.selectedAddress' ng-options='addr for addr in vm.addresses'></select>
        </form>
    </div>