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'];
Related
How to test this factory and controller using karma and jasmine,do I need angular.mocks.js? Is it possible to test witout mocks? And how to make my test look like to test things like this?
Sorry I`m new in testing:(
Service:
app.factory('httpProvide', function ($http) {
function Provider() {
Provider.prototype.getAll = function () {
return $http.get('/all').then(function (response) {
return response.data;
});
}
}
Controller:
function main($scope, httpProvide) {
var httpProvide = new httpProvide.Provider();
var getAll = function () {
httpProvide.getAll().then(function (data) {
$scope.data = data;
});
};
}
I am writing a Factory/Service for the first time and trying to get an image from a factory into controller but getting undefined when in the factory function for some reason.
Here is the fiddle
https://jsfiddle.net/vijay_vadlamani/2qL2Lh5r/1/
Html:-
JS:-
angular.module('myApp').factory('imageFactory', function() {
return function contentImage($scope) {
$scope.getImage = function(content) {
return getContentImage(content);
};
};
});
angular.module(myApp).controller('getImageCtrl', ['$scope', 'imageFactory', function($scope, imageFactory) {
$scope.getListImage = function(content) {
return imageFactory.getImage(content);
};
}]);
Try to declare service like this:
var app = angular.module('myApp',[]);
app.factory('imageFactory', function()
{
var service =
{
getImage: function(content)
{
return getContentImage(content);
}
};
return service
});
and use it like this:
app.controller('getImageCtrl', ['$scope','imageFactory',function($scope, imageFactory)
{
$scope.getListImage = function(content)
{
return imageFactory.getImage(content);
};
}]);
Here's demo
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.
This type of decorator works with services and factories. I expected it to work with providers as well. I've tried the following to decorate ui-router's $stateProvider:
app.config(function($provide) {
$provide.decorator('$state', function ($delegate) {
return $delegate;
});
});
Here's a demo plunk
It should work just the same? See plunk # http://plnkr.co/edit/rSFo1xCoRHjWmrSjJBN1
var app = angular.module('plunker', []);
app.provider('provider', function () {
this.$get = function () {
var provider = {};
var value = 'test';
provider.get = function() {
return value;
}
provider.set = function(param) {
value = param;
}
return provider;
}
});
app.config(function($provide) {
$provide.decorator('provider', function ($delegate) {
$delegate.set('delegate');
return $delegate;
});
});
app.controller('MainCtrl', function($scope, provider) {
$scope.name = provider.get();
});
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