Scope not updating in the view after promise is resolved - angularjs

Even though there is a similar question for this Data is not getting updated in the view after promise is resolved but I am already using this approach and the view is not being updated.
I have a factory:
'use strict';
myApp
.factory('Factory1', [ '$http','$q','$location', '$rootScope', 'Service', function($http, $q, $location, $rootScope, Service){
return {
checkSomething: function(data){
var deferred = $q.defer(); //init promise
Service.checkSomething(data,function(response){
// This is a response from a get request from a service
deferred.resolve(response);
});
return deferred.promise;
}
};
}]);
I have a controller:
'use strict';
myApp
.controller('MyCtrl', ['$rootScope', '$scope', '$location','Service', 'Factory1' function($rootScope, $scope, $location, Service, Factory1) {
if(Service.someCheck() !== undefined)
{
// Setting the variable when view is loaded for the first time, but this shouldn't effect anything
$scope.stringToDisplay = "Loaded";
}
$scope.clickMe = function(){
Factory1.chechSomething($scope.inputData).then(function(response){
$scope.stringToDisplay = response.someData; // The data here is actually being loaded!
});
};
}]);
And the view:
<div class="app " ng-controller="MyCtrl">
{{stringToDisplay}}
<button class="button" ng-click="clickMe()">Update display</button>
</div>
But the data is not being updated in the view when I click on a button "Update display". Why?
Even though the $scope is being loaded with the data
EDIT:
Hm, it seems that I am getting a error when I try $scope.$apply() and it says:
[$rootScope:inprog] $digest already in progress

This might be a digest cycle issue. You could try:
...
$scope.stringToDisplay = response.someData;
$scope.$apply();
...
For the sake of completeness, here is a nice wrap-up of $scope.$apply.
Edit: I tried to reproduce the error in this fiddle, but cannot seem to find any issues. It works flawlessly without $scope.$apply. I use setTimeout in order to simulate asynchronous operations, which should not trigger a digest cycle by itself.

Try this instead of your function
$scope.clickMe =$scope.$apply(function(){
Factory1.chechSomething($scope.inputData).then(function(response){
$scope.stringToDisplay = response.someData; // The data here is actually being loaded!
});
});

Related

Unable to send data to another controller AngularJS

UPDATE PLEASE HELP:
Tried what you suggested, the problem is when i try to pass the value to another "Module". Tried creating a service but the function didn't work, maybe i missed something.
Made Example Using rootScope:
var informes = angular.module('informes', ['angular-drupal', 'ui.bootstrap']);
informes.controller('InformesCtrl', ['drupal', '$rootScope', '$scope', '$http', 'InformesFtry', function(drupal, $rootScope, $scope, $http, InformesFtry) {
$rootScope.nodeID = function(item){
var item = "hi";
console.log(item);
}
}]);
Console hi
It works in first module, but...
In my other Module with different Page...
var nodeInformes = angular.module('node-informes', ['informes']);
nodeInformes.controller('NodeInformesCtrl', ['drupal', '$rootScope', '$scope', '$http', 'InformesFtry', function(drupal, $rootScope, $scope, $http, InformesFtry) {
$scope.nodeID2 = $rootScope.nodeID(item);
console.log($scope.nodeID2);
}]);
Console: item is Undefined
I'm trying to figure out of how can i pass a function to another module, but it seems that it is impossible. I didn't try to use same module, but it works if the controller is child from the first controller.
I really apprecaite any help you can provide me to pass a function with parameter to another module with another controller. I'm trying to learn with this. Thanks!!!
EDIT: If i add the firstcontroller as a dependency it gives me Unknown Provider... Sorry for my mistake.
In Angular JS there are three ways for controllers to communicate with other controllers, and any other method is asking for trouble:
Use a service:
Use a service as an intermediary between controllers:
function MyService() {
this.MyObject = {};
}
function ctrl1(MyService) {
Myservice.MyObject = {
someData: "someValue"
};
}
function ctrl2(MyService) {
$scope.someValue = MyService.MyObject.someData;
}
Use the event system
function ctrl1($rootScope) {
$rootScope.$broadcast("my-event", {someData: "someValue"});
}
function ctrl2($scope) {
$scope.$on("my-event", function(event, params) {
$scope.someData = params.someData;
});
}
Scope inheritence
<div ng-controller="ctrl1">
<div ng-controller="ctrl2">
</div>
</div>
function ctrl1($scope) {
$scope.someData = "someValue";
}
function ctrl2($scope) {
console.log($scope.$parent.someData); //"someValue"
}

$rootScope in AngularJS stays UNDEFINED in Controller despite injection?

It's a small ionic / angularJS project for a mobile app.
I am trying to read a JSON file in the same folder as index.html, then assigning it to a $rootScope variable in the RUN segment and then retrieving the $rootScope variable value in a controller. But it's simply not working despite service injections and all.
This is my JSON file:
{"docs":[{"Name":"Alfreds Futterkiste","City":"Berlin","Country":"Germany"},
{"Name":"Ana Trujillin Moreo","City":"México D.F.","Country":"Mexico"}
]
}
This is my app.js / script.js:
angular.module('starter', ['ionic'])
.run(function($ionicPlatform, $rootScope, $http) {
$ionicPlatform.ready(function() {
$rootScope.myDocs = $http.get("/customers.json")
.success(function (response) {
return response.docs;
});
});
})
.controller('MyCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
$scope.myDocs = $rootScope.myDocs;
//$scope.myDocs = [{'Name': 'a1'},{'Name': 'b1'},{'Name': 'c1'}];
$scope.myStr = JSON.stringify($rootScope.myDocs);
}])
This is the relevant portion from my index.html:
<body ng-app="starter">
<ion-pane>
<ion-header-bar class="bar-stable">
<h1 class="title">Ionic Blank Starter</h1>
</ion-header-bar>
<ion-content ng-controller="MyCtrl">
<ion-list>
<ion-item ng-repeat="x in myDocs">
{{x.Name}}
</ion-item>
</ion-list>
</ion-content>
</ion-pane>
</body>
I have seen other similar examples here but this is still not working and I been banging my head against the wall to achieve a very simple thing.
Fast help is much appreciated.
Here is the Plunker: http://plnkr.co/edit/zx9ANUzCYVvosZFiPSGY?p=info
You're having a timing issue because of ionicPlatform.ready and you aren't using the $http promise correctly.
This is one way to do it:
.run(function($rootScope, $http) {
// $http returns a promise, not the value
$rootScope.docPromise = $http.get("/customers.json")
.then(function(response) {
return response.docs;
});
})
.controller('MyCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
// you get the value within a .then function on the promise
$rootScope.docPromise.then(function(result) {
$scope.myDocs = result;
})
}])
It is unlikely that you need to make this http call within ionicPlatform.ready. If you don't need to, then it is only delaying the call for no benefit. If for some reason you do need to make the call within the ready function, there are numerous ways to solve this. Here is one example:
// inject $q service
var deferred = $q.defer();
$rootScope.docPromise = deferred.promise;
$ionicPlatform.ready(function() {
$http.get(etc).then(function(response) {
deferred.resolve(response.docs);
});
});
// use $rootScope.docPromise as in the previous example
You might be assuming that the return value from the success method on $http.get() will be assigned to myDocs property on $rootScope but it doesn't work that way. $http.get() is an Asynchronous operation and the success callback will be invoked once the response is available. Also the return value from your success callback is not assigned because it happens sometime in the future.
You need to update your ready() like below.
$ionicPlatform.ready(function() {
/* get will return a promise, cache it in on rootScope for use in controller*/
$rootScope.httpPromise = $http.get("/customers.json");
});
In your Controller
.controller('MyCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.httpPromise.then(function(response){
$scope.myDocs = response.data.docs;
});
}]);

Angular factory returning a promise

When my app starts I load some settings from a server. Most of my controllers need this before anything useful can be done. I want to simplify the controller's code as much as possible. My attempt, which doesn't work, is something like this:
app.factory('settings', ['$rootScope', '$http', '$q', function($rootScope, $http, $q) {
var deferred = $q.defer();
$http.get('/api/public/settings/get').success(function(data) {
$rootScope.settings = data;
deferred.resolve();
});
return deferred.promise;
}]);
app.controller('SomeCtrl', ['$rootScope', 'settings', function($rootScope, settings) {
// Here I want settings to be available
}]);
I would like to avoid having a lot of settings.then(function() ...) everywhere.
Any ideas on how to solve this in a nice way?
$http itself return promise you don't need to bind it inside the $q this is not a good practice and considered as Anti Pattern.
Use:-
app.factory('settings', ['$rootScope', '$http', '$q', function($rootScope, $http) {
return $http.get('/api/public/settings/get')
}]);
app.controller('SomeCtrl', ['settings',$scope, function(settings,$scope) {
settings.then(function(result){
$scope.settings=result.data;
});
}]);
Your way can be done as :-
app.factory('settings', ['$rootScope', '$http', '$q', function($rootScope, $http, $q) {
var deferred = $q.defer();
$http.get('/api/public/settings/get').success(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}]);
app.controller('SomeCtrl', ['$scope', 'settings', function($scope, settings) {
settings.then(function(data){
$scope.settings=data;
})
}]);
Don't overload $rootScope if you wanted it you need to use $watch for the changes in $rootScope(Not recommended).
Somewhere you would need to "wait".
The only built-in way in Angular to completely absolve the controller from having to wait on its own for async data to be loaded is to instantiate a controller with $routeProvider's route's resolve property (or the alternative $stateProvider of ui.router). This will run controller only when all the promises are resolved, and the resolved data would be injected.
So, ng-route alternative - plunker:
$routeProvider.when("/", {
controller: "SomeCtrl",
templateUrl: "someTemplate.html",
resolve: {
settings: function(settingsSvc){
return settingsSvc.load(); // I renamed the loading function for clarity
}
});
Then, in SomeCtrl you can add settings as an injectable dependency:
.controller("SomeCtrl", function($scope, settings){
if (settings.foo) $scope.bar = "foo is on";
})
This will "wait" to load someTemplate in <div ng-view></div> until settings is resolved.
The settingsSvc should cache the promise so that it won't need to redo the HTTP request. Note, that as mentioned in another answer, there is no need for $q.defer when the API you are using (like $http) already returns a promise:
.factory("settingsSvc", function($http){
var svc = {settings: {}};
var promise = $http.get('/api/public/settings/get').success(function(data){
svc.settings = data; // optionally set the settings data here
});
svc.load = function(){
return promise;
}
return svc;
});
Another approach, if you don't like the ngRoute way, could be to have the settings service broadcast on $rootScope an event when settings were loaded, and controllers could react to it and do whatever. But that seems "heavier" than .then.
I guess the third way - plunker - would be to have an app-level controller "enabling" the rest of the app only when all the dependencies have preloaded:
.controller("AppCtrl", function($q, settingsSvc, someOtherService){
$scope.loaded = false;
$q.all([settingsSvc.load, someOtherService.prefetch]).then(function(){
$scope.loaded = true;
});
});
And in the View, toggle ng-if with loaded:
<body ng-controller="AppCtrl">
<div ng-if="!loaded">loading app...</div>
<div ng-if="loaded">
<div ng-controller="MainCtrl"></div>
<div ng-controller="MenuCtrl"></div>
</div>
</body>
Fo ui-router this is easily done with having an application root state with at least this minimum definition
$stateProvider
.state('app', {
abstract: true,
template: '<div ui-view></div>'
resolve: {
settings: function($http){
return $http.get('/api/public/settings/get')
.then(function(response) {return response.data});
}
}
})
After this you can make all application states inherit from this root state and
All controllers will be executed only after settings are loaded
All controllers will gain access to settings resolved value as possible injectable.
As mentioned above resolve also works for the original ng-route but since it does not support nesting the approach is not as useful as for ui-router.
You can manually bootstrap your application after settings are loaded.
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
var $rootScope = initInjector.get("$rootScope");
$http.get('/api/public/settings/get').success(function(data) {
$rootScope.settings = data;
angular.element(document).ready(function () {
angular.bootstrap(document, ["app"]);
});
});
In this case your whole application will run only after the settings are loaded.
See Angular bootstrap documentation for details

updating ng-model of one angular js controller from another angular js controller

Angular Experts,
I am new to Angular Js and trying to update input in one controller with value from the input in another controller. I am not sure whether it is even possible or not?
I have created js fiddle link just to give an idea.
http://jsfiddle.net/MwK4T/
In this example when you enter a value in controller 2's input, and click 'Set Value it does update biding with <li></li> but does't not update input of the controller 1.
Thanks in advance
I made it working using $broadcast. And it is working like a charm.
I did something like this.
App.controller('Ctrl1', function($rootScope, $scope) {
$rootScope.$broadcast('msgid', msg);
});
App.controller('Ctrl2', function($scope) {
$scope.$on('msgid', function(event, msg) {
console.log(msg);
});
});
Thanks for helping me out.
Best way would be to use the service, we need to avoid the event as much we can, see this
var app= angular.module('testmodule', []);
app.controller('QuestionsStatusController1',
['$rootScope', '$scope', 'myservice',
function ($rootScope, $scope, myservice) {
$scope.myservice = myservice;
}]);
app.controller('QuestionsStatusController2',
['$rootScope', '$scope', 'myservice',
function ($rootScope, $scope, myservice) {
$scope.myservice = myservice;
}]);
app.service('myservice', function() {
this.xxx = "yyy";
});
Check the working example

Injecting $http and $scope into function within controller

I asked a similar question earlier when attempting to inject $scope and $http into a controller Cannot call method 'jsonp' of undefined in Angular.js controller. Now I'm attempting to refactor that code slightly by moving the code into a function within the controller. I'm encountering similar issues and can't seem to grasp the mechanics of dependency injection in Angular. Below is my new code. Both $scope and $http are undefined. What I'm attempting to do is make an http request when didSelectLanguage() fires and assign the resulting data to the "image" variable in the $scope from the parent controller. Can someone enlighten me as to how dependency injection is supposed to work in this example?
angular.module('myApp.controllers', []).
controller('ImagesCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.didSelectLanguage=function($scope, $http) {
console.log($scope);
$http.jsonp('http://localhost:3000/image?quantity=1&language='+this.language+'&Flag=&callback=JSON_CALLBACK')
.success(function(data){
$scope.image = data;
});
}
}])
When you create your controller:
angular.module('myApp.controllers', []).
controller('ImagesCtrl', ['$scope', '$http', function ($scope, $http) {
// ...
}]);
The stuff inside the body of the controller function automatically has access to $scope and $http because of closures. Thus, there's no need to specify anything additional for a function on the $scope to have access to these things:
angular.module('myApp.controllers', []).
controller('ImagesCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.didSelectLanguage = function() {
$http.jsonp('http://localhost:3000/image?quantity=1&language=' + this.language + '&Flag=&callback=JSON_CALLBACK');
.success(function(data) {
$scope.$parent.image = data;
});
}
}]);
When didSelectLanguage is run, it sees the references to $http, and reaches out of the function into the outer function to get the value of the reference; the same happens for $scope inside the success callback.
So, in short, there's no need to pass any arguments to your didSelectLanguage function, nor is there in this case any reason to use the $injector.
With the help of Michelle Tilley's comment & article I solved the problem as follows. However, I'm going to keep the question open until someone else answers or until I understand enough to write an accompanying explanation.
controller('ImagesCtrl', ['$scope', '$http', '$injector', function ($scope, $http, $injector) {
$scope.didSelectLanguage=function() {
$http.jsonp('http://localhost:3000/image?quantity=1&language='+this.language+'&Flag=&callback=JSON_CALLBACK')
.success(function(data){
$scope.$parent.image = data;
});
}
}])

Resources