I have written a service, depending on an other service. But initialisation is not working.
You can find a plunker as showcase
Should be close to working... Any tipps?
Thanks in advance!
edit: The plunker is fixed now and can be used as reference.
You need to either change your testServiceMockConfig and testService from factory to service, for example:
.service('testServiceMockConfig', function ()
or keep them as factories and add return.this; at the bottom of both of them or restructure them like this (recommended):
angular.module('testServiceMockConfig', [])
.factory('testServiceMockConfig', function() {
console.log("setup cqrs mock config.");
return {
doLoadItems: function(callback) {
console.log("mock loading data");
if (!this.configuredLoadItems) {
throw Error("The mock is not configured to loadItems().");
}
callback(this.loadItemsError, this.loadItemsSuccess);
},
whenLoadItems: function(success, error) {
this.configuredLoadItems = true;
this.loadItemsSuccess = success;
this.loadItemsError = error;
}
};
});
I also assume that loadItems in testService should call:
testServiceMockConfig.doLoadItems(callback);
instead of:
testService.doLoadItems(callback);
As I see from your example,
you didn't define properly the factory. The this key used for service
in testService.doLoadItems(callback); replace with testServiceMockConfig.doLoadItems(callback);
The difference between service - factory - provider and definition you can find in this simple demo:
Fiddle
Fixed example:
angular.module('testServiceMockConfig', [])
.factory('testServiceMockConfig', function () {
console.log("setup cqrs mock config.");
return{
doLoadItems : function (callback) {
console.log("mock loading data");
if (!this.configuredLoadItems) {
throw Error("The mock is not configured to loadItems().");
}
callback(this.loadItemsError, this.loadItemsSuccess);
},
whenLoadItems : function (success, error) {
this.configuredLoadItems = true;
this.loadItemsSuccess = success;
this.loadItemsError = error;
}
}
});
angular.module('testService', ['testServiceMockConfig'])
.factory('testService', ['testServiceMockConfig', function (testServiceMockConfig) {
console.log("mock version. testServiceMockConfig: ");
return {
loadItems : function (callback) {
testServiceMockConfig.doLoadItems(callback);
}
}
}])
angular.module('ItemApp', ['testService'])
.controller('ItemsCtrl', ['$scope', 'testService', function ($scope, testService) {
$scope.text = 'No items loaded';
testService.loadItems(function (error, items) {
if (error) {
$scope.text = "Error happened";
}
$scope.text = '';
for (i = 0; i < items.length; i++) {
$scope.text = $scope.text + items[i].name;
}
})
}]);
Demo Plunker
Related
I'm trying to follow John Papa's Angular styleguide for writing an angular app, but dependency injection is not working for me like this. ShopController and dataservice both have dependencies, but I'm always running into a "cannot read property ____ of undefined" no matter what I try.
angular
.module('shopApp', []);
////////////////////////////////////////////////////////
angular
.module('shopApp')
.controller('ShopController', ShopController);
ShopController.$inject['dataservice'];
function ShopController(dataservice){
var vm = this;
vm.products = [];
vm.getProducts = getProducts;
vm.title = 'test title';
activate();
function activate() {
return getProducts().then(function() {
console.info('Got Products');
});
}
function getProducts() {
return dataservice.getProducts().then(function(data) {
vm.products = data;
console.info(data);
return vm.products;
});
}
}
////////////////////////////////////////////////////////
angular
.module('shopApp')
.factory('dataservice', dataservice);
dataservice.$inject = ['$http'];
function dataservice($http) {
return {
getProducts: getProducts
};
function getProducts() {
return $http.get('/api/products')
.then(getProductsComplete)
.catch(getProductsFailed);
function getProductsComplete(response) {
return response.data.results;
}
function getProductsFailed(error) {
console.error('XHR Failed for getProducts.' + error.data);
}
}
}
The approach does look much cleaner, but I'm puzzled why it's not working properly. Any suggestions?
Replace this:
ShopController.$inject['dataservice'];
by
ShopController.$inject = ['dataservice'];
I want to develop a generic translator component with configurable url and paramsFn. Here paramsFn can either be a plain function or a function with service dependencies. paramsFn is expected to return a promise.
(function () {
"use strict";
angular.module("translator-app", [])
.provider(translatorProvider);
function translatorProvider() {
var
url,
paramsFn;
//Provider Config Functions
function setUrl (pUrl) {
url = pUrl
};
function setParamsFn (pParamsFn) {
paramsFn = pParamsFn;
};
function factory ($http, $q) {
//Service Function Pseudo
function translate(key) {
if (translateions are cached) {
//return promis of cached translations
} else {
/*
make http call with configured url and
paramsFnto fetch translations.
Cache translations.
Return promise with translations.
*/
}
} //translate
//Service Object
return {
translate: translate
};
} // factory
factory .$inject = [
"$http"
"$q"
];
//Exposed functionality
this.setUrl = setUrl;
this.setParamsFn = setParamsFn;
this.$get = factory;
}
}();
An application can use translator after configuring it. User app provide will be able to provide paramFn with service dependencies. paramFn will be invoked later when translator.translate(...) method is called.
(function () {
"use strict";
angular.module('the-app', ["translator-app"])
.config(translatorConfigurator)
.controller(AppController)
function translatorConfigurator (translatorProvider) {
function theParamsFn (someService) {
//use someService to generate and return params object
}
theParamsFn.$inject = [
"someService"
];
translatorProvider.setUrl("/url/to/translator");
translatorProvider.setParamsFn(theParamsFn);
}
function AppController (translator) {
translator.translate("the-key").then(function (translated) {
//do somethid with 'translated'.
});
}
translatorConfigurator.$injec = [
"translatorProvider"
];
AppController.$inject = [
"translator"
];
}());
How can I achieve this?
Short Story:
According to Angular $injector documentation
// inferred (only works if code not minified/obfuscated)
$injector.invoke(function(serviceA){});
// annotated
function explicit(serviceA) {};
explicit.$inject = ['serviceA'];
$injector.invoke(explicit);
// inline
$injector.invoke(['serviceA', function(serviceA){}]);
Novel
Once upon a time there was a poor translatorProvider. Angular, a great super hero, helped translatorProvider to be feature rich by its $injector weapon. translatorProvider built its getParameters function inside factory function and used it in translate.
(function () {
"use strict";
angular.module("translator-app", [])
.provider(translatorProvider);
function translatorProvider() {
var
url,
paramsFn;
//Provider Config Functions
function setUrl (pUrl) {
url = pUrl
};
function setParamsFn (pParamsFn) {
paramsFn = pParamsFn;
};
function factory ($injector, $http, $q) {
function getParameters() {
var
promise,
fn;
if (paramsFn) {
fn = $injector.invoke(paramsFn);
promise = $q.resolve(fn());
} else {
promise = $q.resolve()
}
return promise;
}
//Service Function Pseudo
function translate(key) {
if (translateions are cached) {
//return promis of cached translations
} else {
getParameters()
.then(function (params) {
return $http({
url: url,
params: params
});
})
.then(function (response) {
var extracted = ...; //extract field from response.data
//put extracted into cache
return $q.resolve(extractedField)
});
}
} //translate
//Service Object
return {
translate: translate
};
} // factory
factory .$inject = [
"$injector",
"$http"
"$q"
];
//Exposed functionality
this.setUrl = setUrl;
this.setParamsFn = setParamsFn;
this.$get = factory;
}
}();
Now translator can be configured as below.
(function () {
"use strict";
angular.module('the-app', ["translator-app"])
.config(translatorConfigurator)
.controller(AppController)
function translatorConfigurator (translatorProvider) {
function theParamsFn (someService) {
return function () {
//returns some parameters object
}
}
theParamsFn.$inject = [
"someService"
];
translatorProvider.setUrl("/url/to/translator");
translatorProvider.setParamsFn(theParamsFn);
}
function AppController (translator) {
translator.translate("the-key").then(function (translated) {
//do somethid with 'translated'.
});
}
translatorConfigurator.$inject = [
"translatorProvider"
];
AppController.$inject = [
"translator"
];
}());
After these changes translatorprovider becomes more powerful and help many other modules and they lived happily ever after.
I am new to angularjs. I am having trouble in accessing value that is returned from angularjs service in its controller.
Following is the code in controller:
'use strict';
app.controller('AdminTaskCtrl', function ($scope, Search) {
$scope.buildEnquiry = function (collegeId, ) {
Search.getIdByEmail(collegeId).then ( function ( result ) {
$scope.uId = result;
console.log($scope.uId);
});
};
});//controller ends here
And the code in Search service is as follows:
'use strict';
app.factory('Search',function ($firebase, FIREBASE_URL, $rootScope) {
var simpleuser = "";
getIdByEmail: function(counsellorEmail) {
var collegeuserArray = ($firebase(new Firebase(FIREBASE_URL+"abc/def/")).$asArray());
collegeuserArray.$loaded(function(collegeuserArray) {
for(var i=0; i<collegeuserArray.length; i++)
{
if((collegeuserArray[i].$value) == counsellorEmail)
{
simpleuser = collegeuserArray.$keyAt(collegeuserArray[i]);
console.log(simpleuser);
return simpleuser;
}
}
}, function(error) {
console.error("Error:", error);
});
}
};
);//service ends here.
When the code executes it gives error for .then function as fallows:
TypeError: undefined is not a function and value in controller is not accessed.
Please help.
'use strict';
app.factory('Search',function ($firebase, FIREBASE_URL, $rootScope, $q) {
var simpleuser = "";
getIdByEmail: function(counsellorEmail) {
var deferred = $q.defer();
var collegeUserArray = ($firebase(new Firebase(FIREBASE_URL+"abc/def/")).$asArray());
collegeUserArray.$loaded(function(collegeUserArray) {
for(var i=0; i<collegeUserArray.length; i++)
{
if((collegeUserArray[i].$value) == counsellorEmail)
{
simpleUser = collegeUserArray.$keyAt(collegeUserArray[i]);
console.log(simpleUser);
//return simpleUser;
deferred.resolve(simpleUser);
}
}
}, function(error) {
console.error("Error:", error);
deferred.reject(error);
});
return deferred.promise;
}
};
);
And your controller
'use strict';
app.controller('AdminTaskCtrl', function ($scope, Search) {
$scope.buildEnquiry = function (collegeId, ) {
Search.getIdByEmail(collegeId).then ( function ( result ) {
$scope.uId = result;
console.log($scope.uId);
}, function(error){
//If an error happened, handle it here
});
};
});
To directly answer your question, $loaded returns a promise, so the simplest answer here would be to return it from your service instead of bothering with $q and all these other things.
It does seem like the whole premise here is flawed and that this is an XY problem. It seems like several hacks intended to subvert the intended usage of these libs, and like a good, solid read of the Angular walkthrough and the AngularFire guide would save a lot of pain and needless complexity here.
The use of factory here is a subversion and heavily coupled. The syntax is invalid and wouldn't compile. And ultimately, the goal is to add a search method to the synchronized array returned from AngularFire, which should be done with $extendFactory.
app.factory('firebaseRef', function(FIREBASE_URL) {
return function(path) {
var ref = new Firebase(FIREBASE_URL);
if( path ) { ref = ref.child(path); }
return ref;
}
});
app.factory('SearchableArray', function($firebase, $FirebaseArray) {
var ArrayWithSearch = $FirebaseArray.$extendFactory({
searchByEmail: function(emailAddress) {
var res = null;
for(var i=0, len=this.$list.length; i < len; i++ ) {
if( this.$list[i].email === emailAddress ) {
res = this.$list[i];
break;
}
}
return res;
}
});
return function(ref) {
return $firebase(ref, {arrayFactory: ArrayWithSearch}).$asArray();
}
});
app.controller('Controller', function($scope, SearchableArray, firebaseRef) {
$scope.data = SearchableArray( firebaseRef('abc/def') );
$scope.search = function(email) {
console.log( $scope.data.searchByEmail(email) );
};
});
I'm using a controller to load product data into an $rootScope array. I'm using $http service and works fine, but now I have a new function which fetch the number of products to be loaded. I can't use the function cause the response is slow.
I was wondering if I could use a provider to load the number of products to fetch in the config method before the apps start. And if I could move the $rootScope array to one service. I don't understand Angular docs, they are not really useful even the tutorial at least in providers and services...
app.controller('AppController', [ '$rootScope', '$http', function ( $rootScope,$http) {
$rootScope.empty = 0;
$rootScope.products = [];
$rootScope.lastId = 0;
$rootScope.getLastID = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.lastId = data.lastId;
});
};
$rootScope.getProducts = function () {
if ($rootScope.empty === 0) {
for (i = 1; i < 100; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
$rootScope.products.push(data);
});
}
}
$rootScope.empty.productos = 1;
};
}
I have done this with factory and service but is not working.
app.factory('lastProduct', ['$http', function lastProductFactory($http) {
this.lastId;
var getLast = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
lastId = data.lastId;
});
return lastId;
};
var lastProduct = getLast();
return lastProduct;
}]);
function productList($http, lastProduct) {
this.empty = 0;
this.lastId = lastProduct();
this.products = []
/*this.getLast = function () {
lastId = lastProduct();
};*/
this.getProducts = function () {
if (empty === 0) {
for (i = 1; i < lastId; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
products.push(data);
});
}
}
empty = 1;
return products;
};
}
app.service('productsList', ['$http', 'lastProduct' , ProductsList]);
services are not availables during configuration time, only providers hence you can not use $http to get a value inside the configuration block, but you can use the run block,
you can do
angular.module('app',['dependencies']).
config(function(){
//configs
})
.run(function(service){
service.gerValue()
})
setting the retrieved value inside a service or inside a value is a good idea to avoid contaminate the root scope, and this way the value gets retrieved before the services are instantiated and you can inject the retrieved value as a dependency
Making that many small $http requests does not seem like a good idea. But using a factory to store an array of data to be used across controllers would look something like this. To use a factory you need to return the exposed api. (The this style is used when using a service. I suggest googling the different but I prefer factories). And if you need to alert other controllers that data has changed you can use events.
angular
.module('myApp')
.factory('myData', myData);
function myData($http, $rootScope) {
var myArray = [], lastId;
return {
set: function(data) {
$http
.get('/path/to/data')
.then(function(newData) {
myArray = newData;
$rootScope.$broadcast('GOT_DATA', myArray);
})
},
get: function() {
return myArray
},
getLast: function() {
$http
.get('/path/to/data/last')
.then(function(last) {
lastId = last;
$rootScope.$broadcast('GOT_LAST', lastId);
})
}
}
}
And then from any controller you can inject the factory and get and set the data as you see fit.
angular
.module('myApp')
.controller('MainCtrl', MainCtrl);
function MainCtrl($scope, myData) {
$scope.bindableData = myData.get(); // get default data;
$scope.$on('GOT_DATA', function(event, data) {
$scope.bindableData = data;
})
}
I hope this helps. Let me know if you have any questions.
I done this but not working . rootScope total is undefined when set method is called from some controller.
http://imgur.com/qEl5WV5
But using 10 instead rootscope total
http://imgur.com/t6wB3JZ
I could see that the rootScope total var arrive before the others...
(app_dev.php/api/productos?op=ultimaIdProductos) vs (app_dev.php/api/producto/x)
var app = angular.module('webui', [$http, $rootScope]);
app.run (function ($http, $rootScope){
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.total = data.ultima;
});
});
function myData($http, $rootScope) {
var myArray = [];
return {
set: function () {
console.log($rootScope.total);
for (i = 1; i < $rootScope.total; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
myArray.push(data);
})
}
},
get: function () {
return myArray;
}
}
}
app.controller('AppController', ['$http', '$rootScope', 'myData', function ($http, $rootScope, myData) {
$rootScope.productos = [];
$rootScope.getProductos = function () {
console.log($rootScope.total);
myData.set();
$rootScope.productos = myData.get();
};
}]);
I meet a problem with $resource of angularjs
I have a service factory :
angular.module('publicApp').factory('Model', function ($resource) {
var totalStock = $resource('./resource/data/totalStock',{},{}});
return {
totalStock : totalStock
}
});
And a controller get data from service :
angular.module('publicApp')
.controller('ManageCtrl', function ($scope, Model) {
$scope.total = Model.totalStock.query();
});
then, in another controller, when a function inside it success, I recall the services :
angular.module('publicApp')
.controller('ActionCtrl', function ($scope, Model) {
$scope.checkout = function () {
$http.post('/resource/action/sold', formData).success(function (resp) {
if (resp.resCode == -1) {
alert(resp.result);
return;
}
console.log('sold!');
Model.totalStock.query();
});
}
});
My idea that when Model.totalStock.query() service recall, the $scope.total value of ManageCtrl is updated also. Anyone suggest me do it in right way?
I am not sure, but this may work:
You can create two factories:
'ModelRessource' factory is only used by your 'Model' factory.
angular.module('publicApp').factory('ModelRessource', function ($resource) {
var totalStock = $resource('./resource/data/totalStock',{},{}});
return {
totalStock : totalStock
}
});
Then, you use 'Model' factory to interact with your ressource. If you bind your $scope to Model.totalStack, every time refreshTotalStack() is call, totalStack should be updated.
angular.module('publicApp').factory('Model', function (ModelRessource) {
var data = {};
data.totalStack = {};
return {
totalStack : data.totalStack,
refreshTotalStack : function () {
ModelRessource.totalStock.query(function (resp) {
data.totalStack = resp;
return data.totalStack;
});
}
}
});