Question

What I would like to achieve is set a radio button's state according to the data retrieved from the server in AngularJS.

What makes this more special is that I need to show a div based on a condition (the car has 1 and only 1 assigned to it). If the car is so-called isSingleUser, the div contains the radio buttons needs to be visible, otherwise we need to hide it.

Finally, after a lot of struggling, I even have the solution to achieve this, but I ran into some interesting obstacles what I want to share with others for reference and I still have some questions regarding the alternative "solutions".

  1. The simplest (and working) solution

    We have 3 cars, the Cars service will GET the particular car according to the chosen link. Only car1 and car2 are isSingleUser so the div should only visible for those cars. You can see that the radio button's state changes according to the car.isMonitoringAutoEnablementSet JSON property. This was my first attempt, but I couldn't see the code working at that time, I used the value property of the input, wrongly, because the ng-value is better.

    More specifically, the value property on input cannot handle boolean values as booleans (and not stringified) therefore angular couldn't map the boolean value false to "false" and the same goes with the true value. If I used ng-value, everything is fine. Note that the plateNumber for each car is visible on the page.

    Let's see what I tried afterwards:

  2. Magic with promise

    My assumption was that the data for car is not downloaded yet and the template rendered before the controller received the data and store it to $scope.car

    So I added this code for the controller:

    $scope.car = Cars.get({carId: $routeParams.carId})
        .$promise.then(function(car) {
              $scope.automonitoring_state = car.isMonitoringAutoEnablementSet;
              $scope.isSingleUser = car.isSingleUser;
         });
    

    and modified the view according to the new variables:

    <h1 class="title">{{car.plateNumber}}</h1>
      <div class='ng-hide' ng-show='isSingleUser'>
        <p class='paragraph'>automatic
          <form name="myForm" class="toggle">
            <input type="radio" name="state" ng-model="automonitoring_state" ng-value="true">on
            <input type="radio" name="state" ng-model="automonitoring_state" ng-value="false">off
            <button class="button" disabled>save</button>
          </form>
        </p>
      </div>
    

    Note that the plateNumbers are disappeared from the page, clearly showing that some problem occured while fetching the data for the car variable. Only the new variables had a value in the scope (automonitoring_state and isSingleUser). Somehow even if I put a simple console.log() inside the then function on the promise, the car variable will not hold the data for the car, strange... This solution also seems like a workaround, because I need to define new variables in the scope, but if I save the car's state through the service, I need to sync back my new variables to the $scope.car so this one is definitely a no-go for me.

  3. Resolve with the help of a controller scope function

    Then I googled and googled, found some advices on Stackoverflow, and so on. Hey, there is resolve which is good for passing some variables into controllers before the view is rendered, that's exactly what I want. Naively, I tried to invoke a controller function from the routeProvider like this :

    when('/driver/cars/:carId', {
      templateUrl: 'partials/driver_car.html',
      controller: 'DriverCarController',
      resolve: 'DriverCarController'.resolveCar
    })
    

    and of course added the function in question to the controller:

    $scope.resolveCar = {
            car: ['$routeParams', 'Cars',
                function($routeParams, Cars) {
                    return Cars.get({
                        carId: $routeParams.carId
                    }).then(function(car) {
                        console.log('resolve');
                        return car;
                    }, function() {
                        return [];
                    });
                }
            ]
    };
    

    The result is: nothing, no console.log at all, proving that the function was not invoked.

  4. Another working solution with passing the variable from resolve This trial is a slightly modified version of the solution above. The Cars service is only used from the $routeProvider, if the promise returns the value, it is saved to the car variable.

    .when('/driver/cars/:carId', {
      templateUrl: 'partials/driver_car.html',
      controller: 'DriverCarController',
      resolve: {
      car : function (Cars,$route) {
          return Cars.get({carId: $route.current.params.carId})
                .$promise.then(function (response) {
                   return response;
                });
              }
            }
    })
    

    The only thing to change in the controller is to add 'car' to the injection list and save the parameter to the $scope.

    controllers.controller('DriverCarController', ['$scope', '$routeParams','car', 'SecurityEvents',
      function($scope, $routeParams, car, SecurityEvents) {
        $scope.car = car;
        console.log(car);
      }
    ]);
    

Please see my public plunkers, you can try out each solution I described above: plunkers

Any help is appreciated about judging between the most viable approach, providing some information about the intended use cases of each alternative. Thanks!

Was it helpful?

Solution

Number 2 is the most similar to what I have used.

To fix the bug, just change the code in the controller like this:

Cars.get({
   carId: $routeParams.carId
}).$promise.then(function(car) {
     $scope.automonitoring_state = car.isMonitoringAutoEnablementSet;
     $scope.isSingleUser = car.isSingleUser;
     $scope.car = car;
});

The biggest problem that I see with calling the service from the resolve is that it tightly couples the controller with the app router, which means your controller is less re-useable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top