Async load data into controller - angularjs

I'm currently learning AngularJS and similar stuff, and today I've encountered a problem (probably with async).
What I'm trying to do, is to use an Angular factory to get some data from Firebase and then use the data in a controller.
App.factory('Jobs', ['$firebaseObject', function($firebaseObject) {
var ref = new Firebase('https://myapp.firebaseio.com/Jobs');
return $firebaseObject(ref);
}]);
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
Jobs.$bindTo($scope, 'allJobs');
console.log($scope.allJobs);
}]);
This is working pretty OK. When I put {{ allJobs | json }} in a template- it is updated after few seconds. The problem is that in the controller $scope.allJobs is returning undefined (probably because the response from Firebase arrived later than the code has been executed.
My question is, how to write it, so I can access $scope.allJobs directly in the controller?

You could do something like this:
App.factory('Jobs', ["$firebaseObject",
function($firebaseObject) {
// create a reference to the Firebase where we will store our data
return function(url){
var ref = new Firebase(url);
// this uses AngularFire to create the synchronized array
return $firebaseObject(ref);
};
}
]);
Then in your controller:
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
$scope.allJobs = Jobs('https://myapp.firebaseio.com/Jobs');
$scope.allJobs.$loaded().then();
}]);
This is showing the $loaded method as opposed to $bindTo. As the other answers/comments mention, $bindTo may be the better way to go.

Referencing to this Firebase documentation: https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebaseobject-bindtoscope-varname
I can just do it very very simple:
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
Jobs.$bindTo($scope, 'allJobs').then(function() {
// now I have access to $scope.allJobs when everything is downloaded from Firebase
});
}]);

Related

Return service $http.get response to controller without using $scope

I've been searching around for hours, but I couldn't find a solution. I'm trying to retrieve data from a separate json-file in my AngularJS application. Using the $http I do get the data I want.
However, I want to get specific data from that file at in multiple places. In the service I want to define some functions that controllers can call to retrieve the data (that the service got using $http).
Now the problem is that when I return the all the data to the controller directly, or try to use it later in the service, the assigned variables are undefined.
I try to use controller-as syntax, so I do not want to use $scope. However every solution I've found suggests using $scope. This code now logs
f {$$state: {...}}.
Code of the service:
theApp.service('SettingsService', function($http) {
this.dataVar = $http.get('../settings.json')
.then(function(response) {
return response;
});
});
Code of the controller:
theApp.controller('SomeController', ['SettingsService', function(SettingsService) {
console.log(SettingsService.dataVar);
}]);
UPDATE:
https://jsfiddle.net/md954y0a/
what about calling your service at start-up module then passing to submodules through a service that loads the same instance
html:
<div ng-app='myApp' ng-controller="myctrl">
{{parent}}
<div ng-app='myApp1' ng-controller="myctrl1">
{{myApp1data}}
</div>
</div>
js:
angular.module('myApp', ['myApp1']).controller('myctrl', ['$scope', 'API', function($scope, API) {
$scope.parent = API.getData();
}]).service('API', function($q) {
var object = null;
this.getData = function() {
object = {
obj1: "DATA1",
obj2: "DATA2"
};
return object;
}
});
angular.module('myApp1', []).controller('myctrl1', ['$scope', 'API', function($scope, API) {
$scope.myApp1data = API.getData().obj1
}]);

Why is Angular not updating with a JSON file?

I'm trying to use a simple Angular JS app to load data from a JSON file to a website but it does not work.
The JSON file is:
{"a": "a"}
The Angular app is:
var app = angular.module("app", [])
.controller("ctrl", ["ser", function(ser) {
var vm = this;
ser.getInfo().then(function(data) {
vm.data = data;
});
}])
.service("ser", function() {
this.getInfo = function() {
return $.get("models/model.json");
};
});
The HTML is:
<div ng-controller="ctrl as ctrl">
<p>{{ctrl.data.a}}</p>
</div>
I'm not getting any console errors. I think the problem is related to the lexical scoping for the controller due to the asynchronous getInfo().then() call in the controller, I checked vm inside the function and it is being loaded correctly but doesn't seem to change the ctrl object or Angular is not updating when it does.
I'm serving the app locally.
It works sometimes but most times it doesn't. I can get it to work using $scope but I'm trying to figure out why it's not working now.
It appears you are using jQuery for the ajax. If you modify the scope outside of angular context you need to notify angular to run a digest
Change to using angular $http to avoid such issues
var app = angular.module("app", [])
.controller("ctrl", ["ser", function(ser) {
var vm = this;
ser.getInfo().then(function(response) {
vm.data = response.data;
});
}])
.service("ser", ['$http', function($http) {
this.getInfo = function() {
return $http.get("models/model.json");
};
}]);
DEMO
If it works with $scope that means that without it, Angular is not aware that you performed an asynchronous operation.
I think the following line is using jQuery: return $.get("models/model.json");
So even if you get your data from your function getInfo, it isn't synchronized with the view via vm.data = data;

Firebase and AngularJS, How to retrieve a value from a firebaseArray

It seems to be very very simple. But I can't do it.
I just want to get a value of my Firebase DB in my controller.
The Firebase Database is like this:
users {
30549545 {
name: "Marcelo"
lastName: "Forclaz"
years: 24
}
}
In my controller I wrote the following code:
app.controller('usersCtrl', function($scope, $firebaseArray) {
var ref = firebase.database().ref('users');
$scope.userdata = $firebaseArray(ref);
});
In the ngRepeat of the HTML code the iteration works fine. But I need to get the "years" value in my controller to use it to another command, ¿How can I do it? I've tried of several deferents ways but I didn't get the wished result. I realized that retrieving data from Firebase width AngularJS is not so easy than make it width simple and pure JavaScript.
You have to wait until userdata loaded:
app.controller('usersCtrl', function($scope, $firebaseArray) {
var ref = firebase.database().ref('users');
$scope.userdata = $firebaseArray(ref);
$scope.userdata.$loaded()
.then(function(){
console.log($scope.userdata);
});
});
Finally, I've got it!
$scope.userdata.$ref().once('value', function(snap) {
angular.forEach(snap.val(), function(index)) {
console.log(index.years)
}
}

angular display model on view after getting data from firebase

I am working on displaying collection that I got from DB in angular with firebase DB. I have those controller and service setup. in the html, I use search.users expecting it will hold all the data that I got from the DB but it won't show up. I can't figure out why. I tried few things like angular.copy or $broadcast with no luck. Can anyone help advise on this? Appreciated in advance.
.controller('SearchController', function ($scope, SearchService, logout, $location){
var search = this;
search.users = SearchService.users;
//$scope.$on('evtputUsers', function () {
// search.users = SearchService.users;
//});
})
//service for SearchService
.factory('SearchService', function ($http, $rootScope){
var userRef = new Firebase("app url");
var broadcastUsers = function () {
$rootScope.$broadcast('evtputUsers');
};
//get the user info
//insert the data to the db.
//retrieving the data
var dbUsers;
userRef.child('users').on('value', function(snapshot){
dbUsers = snapshot.val();
// angular.copy(snapshot.val(), dbUsers);
console.log('usersinDB:',dbUsers);
broadcastUsers();
}, function(err){
console.error('an error occured>>>', err);
});
return {
users: dbUsers
};
})
Rather than using $broadcast() and $on() you should use the AngularFire module.
AngularFire provides you with a set of bindings to synchronizing data in Angular.
angular.module('app', ['firebase']) // 1
.controller('SearchCtrl', SearchCtrl);
function SearchCtrl($scope, $firebaseArray) {
var userRef = new Firebase("app url")
$scope.users = $firebaseArray(userRef); // 2
console.log($scope.users.length); // 3
}
There are three important things to take note of:
You need to include AngularFire as firebase in the dependency array.
The $firebaseArray() function will automagically synchronize your user ref data into an array. When the array is updated remotely it will trigger the $digest() loop for you and keep the page refreshed.
This array is asynchronous. It won't log anything until data has populated it. So if you're logs don't show anything initially, this is because the data is still downloading over the network.

Unexpected behaviors of promises

I've been facing a trouble while working with Factory/Service. I've created an AjaxRequests factory for all of my AJAX calls. My factory code is
.factory('AjaxRequests', ['$http', function ($http) {
return {
getCampaignsData: function () {
var campaigns
return $http.get(url).then(function (response) {
campaigns = response.data;
return campaigns;
});
}
}
}])
I've created another service in which I am injecting this factory. My service code
.service('CampaignsService', ['$rootScope', 'AjaxRequests', function ($rootScope, AjaxRequests) {
this.init = function () {
this.camps;
AjaxRequests.getCampaignsData().then(function (response) {
this.camps = response.campaigns;
console.log(this.camps); // It is showing data
})
console.log(this.camps); // But it is not working :(
};
this.init();
}])
And in my controller
.controller('AdvanceSettingsController', ['$scope', 'CampaignsService', function ($scope, CampaignsService) {
$scope.CampaignsService = CampaignsService;
}
])
I've read this article to learn promises but it is not working here. I can directly achieve it in controller and it's been working fine. But it consider as a bad coding standard to make controller thick. But when I use service and factory I stuck. My question is why I am not getting ajax data to use in my whole service ? I need to use CampaignsService.camps in my view template as well as in my whole rest script but every time I get undefined. What is happening here? I've asked the same question before but couldn't get any success. Some one please help me to understand about promises and why I am getting this type of error if I'm working same ? This type of question has already been asked before but it was working in controller. May be I am stuck because I'm using it in a service.
A big thanks in advance.
This is not a bug or some tricky functionality. Just like in any other AJAX implementation, you can only access the response data in AngularJS's $http success method. That's because of the asynchronous nature of Asynchronous JavaScript And XML.
And what you have is working.
.controller('AdvanceSettingsController', ['$scope', 'AjaxRequests', function ($scope, AjaxRequests) {
$scope.camps = [];
AjaxRequests.getCampaignsData().then(function(data) {
$scope.camps = data;
});
}
])
And then bind camps:
<div ng-repeat="camp in camps>{{camp.name}}</div>
What's bad in your implementation is that instead of grouping related stuff in services you are writing a big AjaxRequests service for everything. You should have a CampaignsService that has a getData method and inject that in your controller.
Why is this working? Because $http does a $scope.$apply for you, which triggers a digest cycle after the data is loaded (then) and updates the HTML. So before the then callback that ng-repeat is run with [] and after it it's again run but with data from the response because you are setting $scope.camps = data;.
The reason <div ng-repeat="camp in CampaignsService.camps>{{camp.name}}</div> does not work is because of function variable scoping.
The this reference inside of your then callback is not the same as the this reference outside of it.
This will work and uses the common var self = this trick:
var self = this;
this.camps = [];
this.init = function () {
AjaxRequests.getCampaignsData().then(function (response) {
// here "this" is not the one from outside.
// "self" on the other hand is!
self.camps = response.campaigns;
});
};

Resources