Search code examples
javascriptangularjsangularjs-scopeangularjs-ng-repeatangularjs-routing

Angular View Not Updating with New Data


I'm a bit of an Angular newbie so sorry if this is obvious.

I have a backend which returns an array of objects. After I retrieve them, I move to the next page and i need to display a checkbox for each of the objects. I can successfully retrieve them, so I change the location of browser to the next page where the checkboxes should be displayed but nothing is being displayed. I tried using $scope.$apply() however it throws an error stating that a digest is already taking place but I think that's correct because I've used ng-click which is wrapped in a $scope.$apply() I think?

I can see the services being printed out in the console successfully, however they don't appear in the page. Not sure what I'm doing wrong here?

In my starting view:

<form>
    <div class="form-group">
        <select id="main_service" name="main_service" class="form-control" ng-model="mainService">
             <option>Please Select...</option>
             <option ng-repeat="service in mainServices" value="{[{service}]}">{[{service}]}</option>  
        </select>
    </div>
    <div class="form-group">
        <button class="btn btn-primary" type="submit" ng-click="updateServices()">Continue</button>
    </div>
</form>

in my controller: var professionalSignupControllers = angular.module('professionalSignupCtrls', [])

professionalSignupControllers.controller('professionalQuestions', function($scope, $http, Service, $sce, $routeParams,$location){
$scope.mainService = "";
$scope.services = [];

$scope.updateServices = function(){
    Service.getServices($scope.mainService)
        .success(function(data) {
            $scope.services = data
            console.log($scope.services)
            //$scope.$apply(); throws digest error!
            $location.path( '/services' );
        })
        .error(function(data, error) {
            event.preventDefault();
            alert("Oh Dear")
        });
};

})

In my services partial:

<div>
    <div ng-repeat="service in services" class="checkbox">
        <label><input type="checkbox" name="service[]" value="{[{service.id}]}" ng-model="user.services">{[{service.label}]}</label>
    </div>
</div>

My servicesService (bad name):

angular.module('servicesService', [])

.factory('Service', function($http) {

    return {
        getServices : function(tag) {
            return $http.get('/api/service?searchTerm='+tag)
        }
    }
});

My app config:

app.config(['$routeProvider', '$locationProvider','$interpolateProvider',
function($routeProvider,$locationProvider,$interpolateProvider) {

    $routeProvider.
        when('/start', {
            templateUrl: '/js/partials/start-professional-questions.html',
            controller: 'professionalQuestions'
        }).
        when('/services', {
            templateUrl: '/js/partials/services-professional-questions.html',
            controller: 'professionalQuestions'
        }).
        otherwise({
            redirectTo: '/start'
        });

    $interpolateProvider.startSymbol('{[{').endSymbol('}]}');

    }
]);

I'm using {[{ }]} for interpolation as otherwise it would class with the Blade syntax


Solution

  • You're in angular scope and so you need not trigger $scope.$apply(). it's required only when you're out of angular scope.

    Regarding your issue:

    You're returning the promise object in the service. So the data returned is only available in that controller alone. If you're moving to next page then that dat won't be available.

    If you want to share the data, then you must store that inside the service.

    In your service:

     angular.module('servicesService', [])
    
    .factory('Service', function($http, $q) {
        var data;
        return {
            getData: function() {
               return data;
            },
            getServices : function(tag) {
                var defer = $q.defer();
                $http.get('/api/service?searchTerm='+tag).then(function(response) {
                    data = response;
                    defer.resolve(response);
                });
                return defer.promise;
            }
        }
    });
    

    Now in your another controller, you can access the data using getData()

    In your controller, you may need to modify it a bit:

    $scope.updateServices = function(){
        Service.getServices($scope.mainService).then(function(data){
                $scope.services = data
                console.log($scope.services)
                $location.path( '/services' );
            }, function(data, error) {
                event.preventDefault();
            });
    };
    

    In your second controller, you can access it via Service.getData()

    Since you're using same controller for both views, you need to use an if condition to check the route and get the data alone. Else you may end up firing the service call again.