Andrew
Andrew

Reputation: 319

Angular ng-options ONLY works when ng-repeat operates on same model

I am working on an ASP.Net MVC page that uses a dropdown which currently uses the ng-repeat tag. I'm working to solve the problem where the dropdown does not correctly select the current model value when the page loads so I switched the dropdown to use ng-options.

My new dropdown looks like this:

<select id="one" ng-model="data.CommandProvider"
        ng-options="item.ident as item.ProviderName for item in providers">
</select>

When the page loads my new select displays as a large empty rectangle. It's approximately the width and height to match the three items it should contain but it's not a dropdown. No options and no dropdown button.

However, when I follow the new dropdown with the old dropdown like so:

<select id="one" ng-model="data.CommandProvider"
        ng-options="item.ident as item.ProviderName for item in providers">
</select>

<select id="two" ng-model="data.CommandProvider">
    <option ng-repeat="opt in providers track by opt.ident"
            value="{{opt.ident}}">
      {{opt.ProviderName}}
    </option>
</select>

BOTH dropdowns load their options correctly but NEITHER dropdown correctly displays the current value of the model.

If the page only contains the old dropdown based on ng-repeat that dropdown displays correctly.

I don't understand what could cause such behavior in ng-options and what would cause the dropdowns to never correctly represent the model on page load?

ADDED: So the previous author had mismatched HTML tags and that was causing the error with the new dropdown - why it didn't break the original I don't know. That being said the new dropdown STILL does not display the value of the model when the page is loaded.

Upvotes: 0

Views: 153

Answers (1)

Andrew
Andrew

Reputation: 319

So after working this problem for too long this is the solution that worked for me:

There are three http requests in play: one for each select input and one for the model data and whenever the model data returned before the select data one or both of the select would be out of sync with the model. My solution was to synchronize the data requests.

The select inputs:

<select ng-model="data.Connection">
    <option ng-repeat="opt in connections track by opt.ident" value="{{opt.ident}}">{{opt.ConnectionName}}</option>
</select>

<select id="two" ng-model="data.CommandProvider">
    <option ng-repeat="opt in providers track by opt.ident" value="{{opt.ident}}">{{opt.ProviderName}}</option>
</select>

The javascript:

        // connection and provider data variables
        $scope.providers;
        $scope.connections;

        // function to retrieve connection dropdown data
        $scope.getConnections = function () {
            $scope.getApiData('GetConnections',
                {}, function (data) {
                    $scope.connections = data;
                });
        }

        // function to retrieve the provider dropdown data
        $scope.getProviders = function () {
            $scope.getApiData('GetProviders',
                {}, function (data) {
                    $scope.providers = data;
                });
        }

        // retrieve the primary page data
        $scope.getCommandData = function () {
            $scope.getApiCommandDataV1('GetCommandById',
                {Id: @ViewBag.ID},
                function (data) {
                    $scope.data = data;
                });
        }

        // retrieves data from the core api
        $scope.getApiData = function (alias, params, successFn, errorFn = null) {
            var qdata = { SqlAlias: alias, SqlParameters: params };
            if (errorFn == null) {
                $http.post('/api/request', qdata).success(successFn);
            } else {
                $http.post('/api/request', qdata).success(successFn).error(errorFn);
            }
        }

        // function to request the data for the page
        $scope.init = function () {
            $scope.getConnections();
        }

        // set a watch on the connections variable to fire when the data
        // returns from the server - this requests the Providers information.
        $scope.$watch('connections', function (newValue, oldValue, scope) {
            if (newValue == undefined || newValue == null)
                return;
            $scope.getProviders();
        }, true);

        // set a watch function on the providers variable to fire when the data
        // returns from the server - this requests the primary data for the Command.
        $scope.$watch('providers', function (newValue, oldValue, scope) {
            if (newValue == undefined || newValue == null)
                return;
            $scope.getCommandData();
        }, true);

        // initialize the page logic and data
        $scope.init();

As you can see my use of $scope.$watch forces the data requests to be synchronous rather than asynchronous and using this method insures the two select inputs are correct every time the web page loads.

Feel free to comment on my coding here as there may be better ways to address this problem - just keep in mind that I have only been working with JavaScript and Angular for about a month.

Upvotes: 0

Related Questions