0

I initialize some variables of an AngularJS controller on the server side using ng-init

/* in my server side View */
<div ng-controller="myController" ng-init="myFoo=@myFoo;myBar=@myBar">...</div>

/* in the myApp.js */
app.Controller("myController", function(){
  // wait until myFoo and myBar are initialized, 
  // once defined, perform other tasks

  $scope.$watch("myFoo", function(n,o){}); //?
  $scope.$watch("myBar", function(n,o){}); //?

  // other actions, depending on myFoo and myBar
  for(i=0; i<myFoo; i++) { console.log(myBar); }
});

I need to ensure that when angularjs reaches the for cycle myFoo and myBar variables are already initialized.

Is there a way of doing it (without using strange things like magic=1500 $timeout(magic))?

Here is a CodePen

var app = angular.module("myApp", []);
app.controller("myCtrl", ['$scope', '$timeout', function($scope, $timeout) {


  $scope.myFoo = false;
  $scope.myBar = false;
  
  $scope.$watch("myFoo", function(n,o){
    //$timeout(null,1500);    
    console.log("watch > myFoo from: "+o+" to "+n+"; >"+$scope.myFoo);
  });
  $scope.$watch("myBar", function(n,o){
    //$timeout(null,500);
    console.log("watch > myBar from: "+o+" to "+n+"; >"+$scope.myBar);
  });
  
  console.log("> Main thread: myFoo is: " + $scope.myFoo);
  console.log("> Main thread: myBar is: " + $scope.myBar);
 }]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>

<div ng-app="myApp" ng-controller="myCtrl">
  
  <div  ng-init="myFoo=true;myBar=true"></div>
</div>

as we can see from the execution of that code

> Main thread: myFoo is: false
> Main thread: myBar is: false
watch > myFoo from: true to true; >true
watch > myBar from: true to true; >true

The main thread reaches the variables BEFORE its initialization... Bad !

serge
  • 13,940
  • 35
  • 121
  • 205
  • Why don't you create a service that will return the value from server side? – Vivz Jul 28 '17 at 12:55
  • is a specific data for that view... should I create services for each view? – serge Jul 28 '17 at 12:57
  • Say, I have a partial view that represents a table, and `myFoo` is `maxRowsToDisplay` – serge Jul 28 '17 at 12:59
  • You can send something to your server side for each view(maybe the name of the partial) and get the desired response back from the same service for each of your different views. – Vivz Jul 28 '17 at 13:08
  • You can still render your template with a `` and use `window.myVar` in your controller. Or even create an angular value `app.value('myVar', window.myVar)` so you can use `myVar` in the controller (you have to use it in requirement of the controller). – Pierre Emmanuel Lallemant Jul 28 '17 at 13:23
  • Possible duplicate: https://stackoverflow.com/questions/16729965/how-to-watch-multiple-variable-change-in-angular – Niles Tanner Jul 28 '17 at 13:35

2 Answers2

1

You can fire a function on ng-init.

var app = angular.module("myApp", []);
app.controller("myCtrl", ['$scope', '$timeout', function($scope, $timeout) {


  $scope.myFoo = false;
  $scope.myBar = false;
  
  $scope.$watch("myFoo", function(n,o){
    //$timeout(null,1500);    
    console.log("watch > myFoo from: "+o+" to "+n+"; >"+$scope.myFoo);
  });
  
  $scope.$watch("myBar", function(n,o){
    //$timeout(null,500);
    console.log("watch > myBar from: "+o+" to "+n+"; >"+$scope.myBar);
  });
  
  $scope.load = function(){  
     
        console.log("> Main thread: myFoo is: " + $scope.myFoo);
        console.log("> Main thread: myBar is: " + $scope.myBar);
    
  }
  
 }]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>

<div ng-app="myApp" ng-controller="myCtrl">
  
  <div  ng-init="myFoo=true;myBar=true;load()"></div>
</div>
Gaurav Srivastava
  • 3,232
  • 3
  • 16
  • 36
  • Even if you remove the initialization, the code will work – Vivz Jul 28 '17 at 13:07
  • @Vivz you are right. I did that so it will be clear that ng-init is changing the variable value. – Gaurav Srivastava Jul 28 '17 at 13:10
  • Yeah and do you think this will render the value in view for ng-init ng-init="myFoo=@myFoo;myBar=@myBar"> ? – Vivz Jul 28 '17 at 13:12
  • it will raise $parse:lexerr Lexer Error, – Gaurav Srivastava Jul 28 '17 at 13:14
  • Which backend framework is this and is there a way to render the value like this in angularjs without calling a service but directly getting it from the backend code? – Vivz Jul 28 '17 at 13:16
  • in OP i asked how to be sure that when I execute the `for`, the `myFoo` and `myBar` variables are already initialized (are not undefined or `false`, in your case). – serge Jul 28 '17 at 13:45
  • @Serge from where you want to initialize myFoo, is there any service? Question is not clear about that. if you want to fire a function when your view is loaded you can simply call it on ng-init. – Gaurav Srivastava Jul 28 '17 at 14:04
  • take a look on the updated OP... I initialize the foo and bar in the HTML, in the server side generated view. – serge Jul 28 '17 at 14:23
  • what do you mean by "if"? and "if not", do nothing? – serge Jul 28 '17 at 14:33
  • its just to insure that you have loaded the varible from server side and changed the false value. – Gaurav Srivastava Jul 28 '17 at 14:34
  • ok, but I don't need just to insure, I need to *wait* till the initialization is finished for both variables, and only then do the things – serge Jul 28 '17 at 14:35
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150417/discussion-between-gaurav-srivastava-and-serge). – Gaurav Srivastava Jul 28 '17 at 14:35
  • 1
    works well. however I consider this to be a "hack" :) thanks a lot ! – serge Jul 28 '17 at 15:23
0

You could just wait for both variables to come back and then just destroy the listeners:

var app = angular.module("myApp", []);
app.controller("myCtrl", ['$scope', '$timeout', function($scope, $timeout) {


  $scope.myFoo = false;
  $scope.myBar = false;

  var fooListen = $scope.$watch("myFoo", function(n,o){
    console.log("watch > myFoo from: "+o+" to "+n);
    checkInit();
  });
  var barListen = $scope.$watch("myBar", function(n,o){
    console.log("watch > myBar from: "+o+" to "+n);
    checkInit();
  });

  function checkInit(){
    if($scope.myFoo && $scope.myBar){
      console.log("> Main thread: myFoo is: " + $scope.myFoo);
      console.log("> Main thread: myBar is: " + $scope.myBar);
      //remove the watches
      fooListen();
      barListen();
    }

  }

In this case each time a variable changes you would listen to the change. When both variables are initialized then you can run what ever code you want. But what about the $watches? you can actually tell them to stop listening. For more info on that you can look here: AngularJS : Clear $watch

And here is a updated pen: https://codepen.io/anon/pen/NvGOQE?editors=1112#anon-login

Niles Tanner
  • 3,911
  • 2
  • 17
  • 29
  • your code will not work if the init value will set a "false", not "true" value as it actually does... – serge Jul 28 '17 at 15:25
  • True, but this is only an example, in line with the one you gave me. the logic inside checkInit() could easily be changed to account for this. – Niles Tanner Jul 28 '17 at 15:26
  • sometimes is hard to undestand if a value is initialized or not.... another example is 0 value... so your check may work or may not... – serge Jul 28 '17 at 15:27
  • probably need additional boolean flags to set – serge Jul 28 '17 at 15:32
  • the remove watches I don't really understand how it works... will they not be triggered after the fooListen() execution? – serge Jul 28 '17 at 15:33