I'm having an issue correctly getting a data service to work as I try to follow the Angular Style Guide (https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#data-services)
I'm sure it's something obvious to the more experienced but I can't get the data set to assign properly to the vm.items outside of the
Data Service
(function() {
'use strict';
angular
.module('portfolioApp')
.factory('portfolioService', portfolioService);
portfolioService.$inject = ['$http', 'logger'];
function portfolioService($http, logger) {
return {
getPortfolioData: getPortfolioData,
};
function getPortfolioData() {
return $http.get('./assets/portfolio/portfolioItems.json')
.then(getPortfolioDataComplete)
.catch(getPortfolioDataFail);
function getPortfolioDataComplete(response) {
return response.data;
}
function getPortfolioDataFail(error) {
logger.error('XHR Failed for getPortfolioData.' + error.data);
}
}
}
}());
Controller
.controller('portfolioController', ['$scope', '$http', '$stateParams', 'logger', 'portfolioService', function($scope, $http, $stateParams, logger, portfolioService) {
var vm = this;
vm.items = [];
activate();
function activate() {
return getData().then(function() {
logger.info('Activate the portfolio view');
});
}
function getData() {
return portfolioService.getPortfolioData()
.then(function(data) {
vm.items = data;
return vm.items;
});
}
console.log("test")
console.log(vm.items);
console.log("test")
}])
Your getData function is a promise, so it's run asynchronously. Your console.log are called before the end of the promise so the vm.items is still empty.
Try to put the log in the then callback.
Related
Can't access the scope, for example putting {{ pagec }} on the html isn't working but when I remove blogpostservice from the controller it works fine again.
var app = angular.module('Blog', []);
app.factory('blogpostservice', ['$http', function ($http) {
this.getMoreData = function (pagecount) {
return $http.get('/api/posts/' + pagecount);
}
}]);
app.controller('MainController', ['$scope', 'blogpostservice',
function ($scope, blogpostservice) {
$scope.pagec = 1;
$scope.posts = [];
this.getMoreData = function (posts) {
blogpostservice.getMoreData(pagec).success(function () {
alert('got it successfully!!!');
}).error(function () {
alert('something went wrong!!!');
});
}
}]);
Because you had wrong factory implementation, factory should always return an object. You must have got an error in console(please check).
app.factory('blogpostservice', ['$http',
function ($http) {
function getMoreData (pagecount) {
return $http.get('/api/posts/' + pagecount);
}
return {
getMoreData: getMoreData
}
}
]);
Or you can convert your factory to service, there you need to bind data to this(context) like your were doing before.
app.service('blogpostservice', ['$http', function ($http) {
this.getMoreData = function (pagecount) {
return $http.get('/api/posts/' + pagecount);
}
}]);
Also don't use .success/.error on $http call, they are
deprecated. Instead use .then.
OK, I've built services before but obviously I don't actually know what makes them tick, since I can't seem to debug this ultra-simple service call:
app.js:
var gridApp = angular.module('gridApp', []);
gridApp.controller('mainController', ['$scope', '$http', 'dataService',
function($scope, dataService) {
$scope.message = 'I am Angular and I am working.';
var init = function(){
console.log(dataService.foo);
console.log(dataService.getData());
};
init();
}]);
dataService.js:
(function() {
'use strict';
angular
.module('gridApp')
.service('dataService', dataService)
dataService.$inject = [];
function dataService() {
console.log("I am the dataService and I am loaded");
var foo = 1;
function getData () {
return 2;
}
}
})();
I see this on-screen: I am Angular and I am working. so Angular is loading.
I see this in console: I am the dataService and I am loaded so the dataService is actually being loaded.
But then the console.log is:
undefined (line 8)
TypeError: dataService.getData is not a function (line 9)
What am I missing?
The previous answers are correct in that your $http injection was wrong, but you are also not attaching your service functions to the service:
function dataService() {
var dataService = this; //get a reference to the service
//attach your functions and variables to the service reference
dataService.foo = 1;
dataService.getData = function() {
return 2;
};
}
An Angular service is very simply an object class. It is also a singleton, meaning it's instantiated only once per run of your app. When the service is instantiated it is very much akin to calling the new operator on your dataService "class":
var $dataService = new dataService();
So, when you inject dataService into your controller, you are actually getting an instance, $dataService, of your dataService "class".
See this blog entry for further reading: https://tylermcginnis.com/angularjs-factory-vs-service-vs-provider-5f426cfe6b8c#.sneoo52nk
You are missing the 2nd parameter $http in the function. The named parameters and the actual parameters in function need to be the same, same order and same number. What happened before is that dataService was being assigned an $http instance and the actual dataService was not injected at all because there was no 3rd parameter to inject it into.
var gridApp = angular.module('gridApp', []);
gridApp.controller('mainController', ['$scope', '$http', 'dataService',
function($scope, $http, dataService) {
// ----was missing-----^
$scope.message = 'I am Angular and I am working.';
var init = function(){
console.log(dataService.foo);
console.log(dataService.getData());
};
init();
}]);
We have missed the second param '$http' in function. Just add the '$http' param, it should work fine
var gridApp = angular.module('gridApp', []);
gridApp.controller('mainController', ['$scope', '$http', 'dataService',
function($scope,$http, dataService) {
$scope.message = 'I am Angular and I am working.';
var init = function(){
console.log(dataService.foo);
console.log(dataService.getData());
};
init();
}]);
This is how I've been taught to set up services:
function dataService() {
var dataService = {};
var _foo = 1;
var _getData = function () { return 2; }
dataService.foo = _foo;
dataService.getData = _getData;
return dataService;
}
I believe this facilitates public/private methods/vars.
For reference, this is the full code accessing my service:
app.js:
var gridApp = angular.module('gridApp', []);
// create the controller and inject Angular's $scope
gridApp.controller('mainController', ['$scope', 'dataService', function($scope, dataService) {
// create a message to display in our view
$scope.message = 'Angular is working';
var init = function(){
getPackageData();
};
var getPackageData = function (){
return dataService.getData().then(
function successCallback(response) {
console.log(response);
},
function errorCallback(response) {
console.log(response);
}
);
};
init();
}]);
dataService.js:
(function() {
'use strict';
angular
.module('gridApp')
.service('dataService', dataService)
dataService.$inject = ['$http'];
function dataService($http) {
var dataService = {};
var _getData = function () {
return $http({
method: 'GET',
url: 'data/packages.json'
})
.then(function successCallback(response) {
return response;
},
function errorCallback(response) {
return response;
});
}
dataService.getData = _getData;
return dataService;
}
})();
I'm trying to count the items in an array without using ng-repeat (I don't really need it, i just want to print out the sum).
This is what I've done so far: http://codepen.io/nickimola/pen/zqwOMN?editors=1010
HTML:
<body ng-app="myApp" ng-controller="myCtrl">
<h1>Test</h1>
<div ng-cloak>{{totalErrors()}}</div>
</body>
Javascript:
angular.module('myApp', []).controller('myCtrl', ['$scope', '$timeout', function($scope) {
$scope.tiles= {
'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]}
$scope.totalErrors = function() {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
})
}
}
}]);
This code works on codepen, but on my app I get this error:
Cannot read property '0' of undefined
and if I debug it, topLevel is undefined when the functions is called.
I think it is related to the loading of the data, as on my app I have a service that looks like this:
angular.module('services', ['ngResource']).factory('tilesData', [
'$http', '$stateParams', function($http, $stateParams) {
var tilesData;
tilesData = function(myData) {
if (myData) {
return this.setData(myData);
}
};
tilesData.prototype = {
setData: function(myData) {
return angular.extend(this, myData);
},
load: function(id) {
var scope;
scope = this;
return $http.get('default-system.json').success(function(myData) {
return scope.setData(myData.data);
}).error(function(err) {
return console.error(err);
});
}
};
return tilesData;
}
]);
and I load the data like this in my controller:
angular.module('myController', ['services', 'ionic']).controller('uiSettings', [
'$scope', '$ionicPopup', '$ionicModal', 'tilesData', function($scope, $ionicPopup, $ionicModal, tilesData) {
$scope.tiles = new tilesData();
$scope.tiles.load();
$scope.totalErrors = function() {
debugger;
var topLevel;
topLevel = $scope.tiles.data;
console.log(topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length;
}).reduce(function(prev, curr) {
return prev + curr;
});
};
}
]);
but I don't know what to do to solve this issue. Any help will be really appreciated. Thanks a lot
The $http.get() method is asynchronous, so you can handle this in your controller with a callback or a promise. I have an example using a promise here.
I've made an example pen that passes back the sample data you use above asynchronously.This mocks the $http.get call you make.
I have handled the async call in the controller in a slightly different way to what you had done, but this way it works with the .then() pattern that promises use. This should give you an example of how you can handle the async code in your controller.
Note as well that my service is in the same module as my controller. This shouldn't matter and the way you've done it, injecting your factory module into your main module is fine.
angular.module('myApp', [])
//Define your controller
.controller('myCtrl', ['$scope','myFactory', function($scope,myFactory) {
//call async function from service, with .then pattern:
myFactory.myFunction().then(
function(data){
// Call function that does your map reduce
$scope.totalErrors = setTotalErrors();
},
function(error){
console.log(error);
});
function setTotalErrors () {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
});
}
}
}])
.factory('myFactory', ['$timeout','$q',function($timeout,$q){
return {
myFunction : myFunction
};
function myFunction(){
//Create deferred object with $q.
var deferred = $q.defer();
//mock an async call with a timeout
$timeout(function(){
//resolve the promise with the sample data
deferred.resolve(
{'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]})
},200);
//return promise object.
return deferred.promise;
}
}]);
Have a look : Link to codepen
Also, have a read of the $q documentation: documentation
App.factory('menuService', function ($http) {
var urlBase = 'Services/MenuService.asmx/GetAllMenu';
var factory = {};
factory.getAllMenus= function () {
return $http.get(urlBase);
};
return factory;
});
Controller:
App.controller("sampleController", function ($scope, menuService) {
$scope.List = [];
var menuData=function(data, status){
$scope.List = data;
console.log($scope.List);
}
menuService.getAllMenus().success(menuData);
});
/// Working perfect...
How can i use same service by other controller?
I've tried this one but wrong...
App.controller("viewDetailMenu", function ($scope, menuService, $stateParams) {
$scope.menu = menuService.getMenu($stateParams.id);
});
Here I share image how it look..
Please help me!...
You need to have all functions/methods defined if you want to use them. You getMenu function/method is not defined so it will generate an error. Please look at below code. You can add number of functions. You factory is share by all controllers so you can use it in any controller.
App.factory('menuService', function ($http) {
var urlBase = 'Services/MenuService.asmx/GetAllMenu';
var factory = {};
factory.getAllMenus= function () {
return $http.get(urlBase);
},
factory.getMenu=function(id){
return $http.get(urlBase +"/ID="+ id) // write it according to your API.
}
return factory;
});
And then,
App.controller("viewDetailMenu", function ($scope, menuService, $stateParams) {
$scope.menu = menuService.getMenu($stateParams.id).success(function(data,status){
}).error(function(data,status){
});
});
I have defined a service with functions like this:
angular.module('common').factory('_o', ['$angularCacheFactory', '$http', '$q', '$resource', '$timeout', '_u',
function ($angularCacheFactory, $http, $q, $resource, $timeout, _u) {
var _getContentTypes = function ($scope) {
var defer = $q.defer();
$http.get('/api/ContentType/GetSelect', { cache: _u.oyc })
.success(function (data) {
$scope.option.contentTypes = data;
$scope.option.contentTypesPlus = [{ id: 0, name: '*' }].concat(data);
$scope.option.sContentType = parseInt(_u.oyc.get('sContentType')) || 0;
defer.resolve();
})
return defer.promise;
};
return {
getContentTypes: _getContentTypes
}
}]);
I am calling this in my controller like this:
.controller('AdminProblemController', ['$http', '$q', '$resource', '$rootScope', '$scope', '_g', '_o', '_u',
function ($http, $q, $resource, $rootScope, $scope, _g, _o, _u) {
$scope.entityType = "Problem";
_u.oyc.put('adminPage', $scope.entityType.toLowerCase());
$q.all([
_o.getContentTypes($scope),
_o.getABC($scope),
_o.getDEF($scope)
])
Am I correct in saying this is not the best way to use a service. I think I should be returning the
content type data and then in the controller assigning to the scope not in the service.
But I am not sure how to do this as my service just returns a defer.promise and I am using $q.all so I think I should populate the scope after $q.all has returned success for every call.
Can someone give me some advice on how I should return data from a service with a promise and have it populate the $scope after $q.all has completed with all calls successful ?
You are absolutely correct in saying that the controller should really be doing this, it would be much cleaner to remove the passing around of your scope (and make it more re-usable). I don't know your exact use case and it is a little confusing to read, but you can do this by hooking into the promises that are created by $http, as well as still handling when all of the promises have been completed.
fiddle: http://jsfiddle.net/PtM8N/3/
HTML
<div ng-app="myApp" ng-controller="Ctrl">
{{model | json}}
<div ng-show="loading">Loading...</div>
</div>
Angular
var app = angular.module("myApp", []);
app.service("_service", ["$http", function (http) {
this.firstRequest = function () {
return http.get("http://json.ph/json?delay=1000")
.then(function (res) {
// manipulate data
res.data.something = new Date();
return res.data;
});
};
this.secondRequest = function () {
return http.get("http://json.ph/json?delay=2000")
.then(function (res) {
// manipulate data
res.data.something = 12345;
return res.data;
});
};
this.thirdRequest = function () {
return http.get("http://json.ph/json?delay=3000")
.then(function (res) {
// manipulate data
res.data.something = "bacon";
return res.data;
});
};
}]);
app.controller("Ctrl", ["$scope", "_service", "$q", function (scope, service, q) {
scope.loading = true;
scope.model = {};
var firstRequest = service.firstRequest();
var secondRequest = service.secondRequest();
var thirdRequest = service.thirdRequest();
q.all([firstRequest, secondRequest, thirdRequest]).then(function (responses) {
scope.model.first = responses[0];
scope.model.second = responses[1];
scope.model.third = responses[2];
scope.loading = false;
});
}]);