I am defining a service in AngularJS 1.2.x like below where I inject the DreamFactory API for use in this service. Now, when I do this, I get the error message like
TypeError: Cannot read property 'getRecords' of undefined
I interprete that the injection has not completely terminated. So I went ahead and delayed the call to the API 1000ms which solves the problem. But of course, this delay is not an acceptable solution.
Somehow I need an event for injection completion upon which I can then launch the DreamFactory.api.db.getRecord() method.
Can anyone help?
(function () {
'use strict';
angular
.module('app')
.service('VisitService', VisitService);
VisitService.$inject = ['$cookieStore', '$rootScope', '$timeout', 'DreamFactory'];
function VisitService($cookieStore, $rootScope, $timeout, DreamFactory) {
var service = {};
service.getVisits = getVisits;
function getVisits(callback) {
//setTimeout(function(){
DreamFactory.api.db.getRecords({table_name: 'visitors'})
.then(function(result) {
console.log(result.data.record);
callback(result.data.record)
},
function(reject) {
});
//}, 1000);
}
return service;
}
})();
in the controller, I call the service like so
VisitService.getVisits(function(result){
alert("this callback was triggered")
console.log(result)
});
DreamFactory uses, besides being a promise based API, another condition that one has to look for.
I do this with :
$scope.$on('api:ready', function (e) { }
This watcher being present solves my problem.
Thanks everybody who tried to help
Related
I got an app.run where i get my data from an webintent. In the app.run i'm not allowed to use $scope so i use $rootscope.Now my $scope got $scope.sendURL(Object) and I want to make this call inside my app.run.
I searched a lot on stackoverflow and google and I came acros $broadcast and $emit. But i can't figure out if that is right thing to do. Does somebody know what i should try?
edit:
I need to call the $scope.sendURL inside app.run and i have no idea how to do it. I searched on stackoverflow and came acros $broadcast and $emitbut i'm not use if i need to use them.
Move the logic into a factory and use the factory in run and in your controller:
var app = angular.module('myApp', []);
app.run(function (myFactory) {
var data = {};
myFactory.sendURL(data);
});
app.controller('MyController', function ($scope, myFactory) {
$scope.sendURL = myFactory.sendURL;
});
app.factory('myFactory', function ($http) {
return { sendURL : sendURL };
function sendURL(data) {
// Put the logic here
console.log(data);
}
});
In my application, I'm getting some data in app.run using $http.get()
The result from this is required to proceed to the controller.
Currently my controller is executing before the completion of this $http.get
How can I make my controller's execute after the execution of $http.get()
app.run(function ($rootScope, $http, $cookies) {
var currentLanguageId = angular.fromJson($cookies.get('mykey')).UserInfo.Country;
$http.get('myurl').then(function (serviceInfo) {
$rootScope.serviceURL = serviceInfo.data.serviceURL;
}, function (error) {
alert('error service info');
});
run can't handle asynchronous work at the moment. See this issue : https://github.com/angular/angular.js/issues/4003
You have multiple solutions for that. You can get your data without Angular and start Angular manually ( How to wait for a promise in a Run block in Angular? )
Or you can use the resolve of your route to do this : AngularJS : Initialize service with asynchronous data
You can use angular scope event. When data is fetched, you can broadcast an event from $rootScope to all controllers and receive this event in target controller.
You may use $rootScope.$broadcast();in the app.run and then use $rootScope.$on() in your controller.
app.run(function ($rootScope, $http, $cookies) {
var currentLanguageId = angular.fromJson($cookies.get('mykey')).UserInfo.Country;
$http.get('myurl').then(function (serviceInfo) {
$rootScope.serviceURL = serviceInfo.data.serviceURL;
$rootScope.$broadcast('serviceInfoReceived')
}, function (error) {
alert('error service info');
});
});
In your Controller
app.controller ('myCtrl' , function($rootScope) {
$rootScope.$on("serviceInfoReceived", function(){
console.log($rootScope.serviceURL)
});
})
Hope this may help you.
This is an old question, however the $rootScope.$on solution will not always work. It will depend on the timing of the controller registering the listener. The way I have found that works is to set a $rootScope property, and then configure a recursive timeout to wait for it to be set.
function waitForRun() {
if($rootScope.runFinalized){
// do something
} else {
$timeout(waitForRun, 500)
}
}
waitForRun();
and after the last .run block:
.run(function($rootScope) { $rootScope.runFinalized = true; })
Ugly, but it works.
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;
});
};
I have this piece of code:
.service('webSocket', function ($rootScope, socketFactory, CONFIG, $timeout) {
angular.element(document).ready(function () {
$rootScope.log('Waiting for connection...',CONSOLE_INFO);
});
And I am getting this error:
TypeError: $rootScope.log is not a function
This service is injected into this controller:
.controller('mainCtrl', function mainCtrl($scope, $rootScope, webSocket, myConsole ...
In which I have:
$rootScope.log = function (msg, type) { myConsole.log(msg,type); ... };
Can you tell me where is the problem? Or at least point me in the right direction? The reason I am using document ready function is because apart from logging messages to browser console (console.log) I use notifications for user (pNotify library) which needs to be called after DOM is loaded.
Sharing something between services using $rootScope should be considered generally as anti-pattern. If you don't have some different implementation of console for different controllers, you can do it Angular-way and perform all configurations in config block. Subscribing to document ready event in the service is also not a good idea (I would prefer to do it in run block), since in angular service is instantiated once it is first time required by any other service or controller or whatever. In order to have configurable service that may have different console implementation I would implement it using provider as follows:
angular.module('app',[]).
constant('console', console).
constant('PNotify', PNotify).
provider('myConsole', function() {
var log = angular.noop;
function MyConsoleFactory() {
return {
log: log,
debug: log
}
}
this.setLog = function(logImplementation) {
log = logImplementation
}
this.$get = [MyConsoleFactory];
}).
config(['myConsoleProvider', 'console', 'PNotify', function(myConsoleProvider, console, PNotify) {
myConsoleProvider.setLog(function(msg) {
console.log('[LOG] '+ Date.now() + ':\t' + msg);
new PNotify({
title: 'Title',
text: msg
});
});
}]).
run(['myConsole', '$document', function(myConsole, $document) {
$document.ready(function () {
myConsole.log('Waiting for connection...');
});
}]);
In this case you don't need any controller at all.
Plunker: http://plnkr.co/edit/aV9TIO07pnDs26xDBPtf?p=preview
That happens because service code runs before service was added to controller(where $rootScope.log method is defined). You can move $rootScope.log = function (msg, type) { myConsole.log(msg,type); ... }; into app.run(...) method and it will work.
I'm still wrapping my brain around Angular.JS.
I have two independent $http calls that retrieve data from remote web services. I have an action that I want to fire off after both service calls have been completed.
The first service call will populate the $scope.model
The second service call, modifies data in the $scope.model (it adds some counter properties that are rendered in the view)
Another unique requirement is that eventually the second service call will be called and updated outside the controller with the $scope.model. It's a notification message pump.
I'm guessing I'm going to use promises $q and possibly $service, but I'm not really sure where to start for something like this following some best practices.
I know it doesn't sound like async calls are appropriate here, since my example it could be simplified by doing it syncronously. However, the second service call is a notification updater, so it'll get continually polled to the server (eventually a websocket will be used).
It's a common pattern I'll see in this application.
You'll want to use $q promises. Specifically $q.all(). All $http methods will return promises. $q.all([promise, promise, promise]).then(doSomething) will wait for all promises to resolve then call doSomething passing an array of the promises results to it.
app.service('myService', ['$http', '$q', function($http, $q) {
return {
waitForBoth: function() {
return $q.all([
$http.get('/One/Thing'),
$http.get('/Other/Thing')
]);
};
}
}]);
Then call it:
app.controller('MyCtrl', ['$scope', 'myService', function($scope, myService) {
myService.waitForBoth().then(function (returnValues){
var from1 = returnValues[0].data;
var from2 = returnValues[1].data;
//do something here.
});
}]);
Here's a demonstration Plunker for you.
Create a service for your first service call. Pete's answer will help with that: https://stackoverflow.com/a/12513509/215945
In your controller, in the then() callback, add a $watch for the appropriate $scope.model property:
app.controller('MainCtrl', function(myService, $scope) {
myService.async().then(function(myData) {
$scope.model = myData;
$scope.$watch('model.???', function(newVal, oldVal) {
if(newVal !== oldVal) {
// do something here, now that $scope.model.??? has changed
}
})
});
});