Async variable stored in $rootScope not available in other controllers - angularjs

I am using Angular 1.x for my stack and when I make an API call and store the response in the $rootScope, it is not accessible in other controllers' view.
My controller:
angularApp.controller('mainController', ['$scope', '$rootScope', '$http', function($scope, $rootScope, $http){
var checkIfAuthenticated = function(){
return $http.get('/api/isAuthenticated');
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
}]);
Now, in another controller's view I use it like this:
<div ng-if="userLoggedIn" class="py-1 pl-1 pr-1">
<span><b>Message Board</b></span>
<div class="form-control" readonly id="lockTextbox">Show something</div>
</div>
The problem is, the API call /api/isAuthenticated does provide the right response (status 200) but the ng-view gets it wrong.
I am sure this is to do with $rootScope.userLoggedIn being a response from an async call (as I get Success in my console) but how do I solve it?
Thanks in advance!
EDIT
After I posted the question, I noticed that in the mainController's view, the ng-if logic doesn't work either. What is very strange is that when I open up the browser's debug console, it starts working! I think this is just an async issue but I don't know how to solve it. How do I tell the view that the variable is on it's way?

OK, to solve the timing issue, I'll rework the answer completely. This should be a - quick and dirty but - working example:
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="plunker" ng-cloak>
<div ng-controller="MainCtrl as $ctrl">
<h1>Hello {{$ctrl.name}}</h1>
<p>Start editing and see your changes reflected here!</p>
<div ng-if="$ctrl.name === 'Angular.js'"><b>Message Board</b</div>
</div>
</body>
</html>
script.js
import angular from 'angular';
angular.module('plunker', []).controller('MainCtrl', function($scope, $http) {
const self = this;
self.name = 'Plunker';
console.log('hello');
function checkIfAuthenticated(){
console.log('get');
return $http.get('https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js');
};
checkIfAuthenticated().then(function(res) {
if(res.status===200){
console.log('Success');
self.name = 'Angular.js'; // insert any logic here
} else {
console.log('Failed');
}
})
});
Console
hello
get
Success
Does it work for you?

Working Example
The below demo shows the $rootScope variable available in both controllers after being set from a promise delayed by two seconds.
angular.module("app",[])
.controller('mainController', function($scope, $rootScope, $timeout) {
var checkIfAuthenticated = function(){
return $timeout(function() {
return { status: 200 };
}, 2000);
};
checkIfAuthenticated()
.then(function(res) {
if(res.status===200){
console.log("Success");
$rootScope.userLoggedIn = true;
}
})
})
.controller('otherController', function($scope) {
console.log("other controller");
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<fieldset ng-controller="mainController">
MAIN Controller userLoggedIn={{userLoggedIn}}<br>
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
<fieldset ng-controller="otherController">
OTHER Controller userLoggedIn={{userLoggedIn}}
<div ng-if="userLoggedIn">
Message Board - Show something
</div>
</fieldset>
</body>

I found the problem. I updated my Angular from 1.6.4 to 1.7.9 (and all modules like angular-sanitize etc) and that did the trick. Should have been the first thing I did but I missed it entirely

Related

Why is pushState disrupted when using ng-include?

When using ng-include, the pushState function is disrupted.
The following is a simple example of this.
<div ng-app="angularApp" ng-controller="angularCtrl">
<div ng-include="templateUrl"></div>
<button ng-click="bye()">BYE</button>
</div>
and
let app = angular.module('angularApp', []);
app.controller('angularCtrl', async function ($scope) {
$scope.templateUrl = '/assets/hi.html';
$scope.bye = function () {
$scope.templateUrl = '/assets/bye.html';
history.pushState({},'','/finish.html')
};
});
We want to change the body value after pressing the BYE button using ng-include and also change the page address by pushState.
This example is part of a larger project that has been simplified here as much as possible.
Note: According to the reviews, the url is changed by pushState but immediately returns its value. It is ignored during the ng-include process.
Changing the browser location is inherently tricky in AngularJS. It is best to try to use the $location service as much as possible as this understands and works with the $scope digest machinery better.
Try using the following in your app:
let app = angular.module('angularApp', []);
app.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
app.controller('angularCtrl', function ($scope, $timeout, $location) {
$scope.templateUrl = '/assets/hi.html';
$scope.bye = function () {
$scope.templateUrl = '/assets/bye.html';
$location.url('/finish.html');
};
});
Note that you will need to provide a base[href] for html5 mode support:
<html lang="en">
<head>
...
<base href="/">
</head>
<body>
<div ng-app="angularApp" ng-controller="angularCtrl">
<div ng-include="templateUrl"></div>
<button ng-click="bye()">BYE</button>
</div>
</body>
</html>

AngularJS JSFiddle and http service

I'm working on an angularJS app and I can't seem to retrieve my JSON object from the url. Not sure what I'm doing wrong...
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope, $http) {
var url = "https://jsfiddle.net/Afroza/j35e8v8h/r";
$http.get(url).then( funtion(response) {
$scope.info = response.data;
})
});
Here's my html:
<div ng-app="app" ng-controller="mainCtrl">
<div>
<h1>Hello World</h1>
<p>Data: {{info}}</p>
</div>
</div>
Also, I'm using JSFiddle so all the html tags and angularjs scripts are included.
You spelling of function is wrong. Its funtion in your code.
$http.get(url).then( function(response) {
$scope.info = response.data;
})
You can always check console ctrl+shift+j in chrome to check syntax and other js related errors.
Working fiddle : https://jsfiddle.net/j35e8v8h/18/

Play Framework + Angular Issue with JSON render on Page

I have simple Scala Play Framework and Angular application. I tried to render JSON data on play's "xxx.scala.html" template but don't know what is the problem it is not rendering as expeted.
#main("Welcome to Play") {
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"> </script>
<script>
app.controller('NamesCtrl', function($scope) {
// get names using AngularJS AJAX API
$http.get('/getNames').success(function(data){
$scope.names = data;
});
});
</script>
<div ng-app="app" ng-contoller="NamesCtrl">
<ul>
<li ng-repeat=" name in names">{{name}}</li>
</ul>
</div>
}
My route entry
GET /getNames controllers.HomeController.getNames
Scala Controller:
def getNames = Action {
val names = List("Bob","Mike","John")
Ok(Json.toJson(names)).as(JSON)
}
When I am calling my page using url
http://localhost:9000/getNames
I was getting response on page as below,
["Bob","Mike","John"]
Please can you explain what am I doing wrong here?
Thanks !!
There are some problems in the code. The correct one is this:
#main("Welcome to Play") {
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.19/angular.min.js"></script>
<div ng-app="myApp" ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">{{name}}</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('NamesCtrl', function($scope, $http) {
$http.get('/getNames').success(function(data){
console.log(data);
$scope.names = data;
});
});
</script>
}
What has changed:
AngularJS 1.3.19 instead of 1.2.10
AngularJS module - which is referenced in the div
injected the $http service in the controller
your ng was wrong - it should be ng-controller instead of ng-contoller (a typo I guess)
The result is what you would expect:

Angular binding error

I have a curious error on my angular app.
I have a controller where I declare some variables. If I am going to write them in the html it works as expected. But if I try to change them, eg in a input-box nothing happens.
Even when I try to call a function from a button or something nothing happens.
The code below is a simplified version of my controller.
angular.module('myModule', [])
.controller('myCtrl', ['$rootScope', '$scope', 'SomeServices',
function ($rootScope, $scope, someServices) {
'use strict';
$scope.myVar = "foobar";
$scope.myFunction = function() {
// it never gets here
}
$scope.$watch("myVar", function() {
// it even never gets here
});
}]);
My HTML
<!-- change this and the watcher will not doing anything -->
<!-- the value will bee foobar, but after that nothing happens -->
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
If I tried it in codepen it works normaly.
Has anyone even got such a behaviour?
I don't see any problem why it should not work, may be this is more simplified demonstration,
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.myVar = "foobar";
$scope.myFunction = function() {
console.log($scope.myVar + ' myFunction called')
}
$scope.$watch("myVar", function() {
console.log($scope.myVar + ' value changed')
});
});
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9">
</script>
<div ng-app="myApp" ng-controller="MainCtrl">
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
</div>
Open developer tool to see logs.
Elaborate more if you are facing some other problem.

Very simple ng-model watch not working

Here is the jsfiddle.
http://jsfiddle.net/CLcfC/
code
var app = angular.module('app',['']);
app.controller('TestCtrl',function($scope){
$scope.text = 'Change Me';
$scope.$watch('text',function(){
alert('Changed !');
});
})
HTML
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="TestCtrl">
<input type="text" ng-model='text'/>
<span>{{text}}</span>
</div>
</div>
I am not able to see the change in $scope.text. Please help.
This is so easy but what am I missing?
Change the module creation to this, make sure you don't put a empty string in the []. (Obvious the empty string is not a module that can be injected.)
var app = angular.module('app', []);
Demo: http://jsfiddle.net/MWa66/
Your JavaScript file loads after the AngularJS initialization and that's why it fails to find your module. In order to fix it change the initialization to a manual initialization.
First change your HTML and remove the ng-app directive:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<div id="appRoot">
<div ng-controller="TestCtrl">
<input type="text" ng-model='text'/>
<span>{{text}}</span>
</div>
</div>
Then go to your JavaScript and use angular.bootstrap method to manually attach your module:
var app = angular.module('app',[]);
app.controller('TestCtrl',function($scope){
$scope.text = 'Change Me';
$scope.$watch('text',function(){
alert('Changed !');
});
});
angular.element(document).ready(function() {
angular.bootstrap(document.getElementById('appRoot'), ['app']);
});
You can find more help on manual AngularJS initialization here.
Thank you! I solved this annoying thing!
The solution that worked for me was that I use angular UI router and there I had used the following code
.state('app.monimes', {
url: "/monimes",
views: {
'menuContent' :{
templateUrl: "templates/monimes.html",
controller: 'sampleCtrl'
}
}
})
so then in the controller I had
/***
*
*Controller for tests..
*/
.controller('sampleCtrl',['$scope','sampleService', function($scope, $sampleService) {
$scope.username="em";
// Watch for changes on the username property.
// If there is a change, run the function
$scope.$watch('username', function(newUsername) {
// uses the $http service to call the GitHub API
// //log it
$scope.log(newUsername);
// and returns the resulting promise
$sampleService.events(newUsername)
.success(function(data, status, headers) {
// the success function wraps the response in data
// so we need to call data.data to fetch the raw data
$scope.events = data.data;
});
},true);
}
]);
and in the view I had
<div>
<label for="username">Type in a GitHub username</label>
<input type="text" ng-model="username" placeholder="Enter a GitHub username, like a user" />
<pre ng-show="username">{{ events }}</pre>
</div>
but that didn't work.
so I added ng-controller="sampleCtrl"
to the div and now it works :D
so that means that the view is loaded after the controller loads and the watcher doesn't get added to the watching variable.

Resources