Angular, prepopulate data with .run then pass into factory - javascript

New to angular here. I am trying to data then pass it into a factory for use. I am trying to get the events to fire in a certain order with using the $http within angular. I was pointed in the direction of using .run to do the first population, pass it into the factory, which then passes it into the controllers. The factory has a function which then would allow me to "refresh" the data being shared with the controllers.
I received some great help setting up the refresh function, but I am struggling get the data to populate initially. Here is my attempt:
.run("getDataForLevels", function($http){
var getIt = $http.get("/assets/images/generated.json")
.success(function(data){
return data;
});
return getIt;
})
.factory("UserService", function($http, getDataForLevels) {
dataFactory = getDataForLevels;
dataFactory.currentData = null;
dataFactory.update = function () {
return $http.get("/assets/images/generated.json")
.success(function(data){
dataFactory.currentData = data;
return data;
});
};
return dataFactory;
});
I then Add the dependency of UserService to the controllers and pass in the data to the controllers. I seem to be betting an error [ng:areq] on this. Any/all help would be much appreciated. Thanks!!

you can't do like this :
.run("getDataForLevels", function($http){
var getIt = $http.get("/assets/images/generated.json")
.success(function(data){
return data;
});
return getIt;
})
it only take one argument, as a function.
you can do like this:
.run(function($http,$rootScope){
$http.get("/assets/images/generated.json")
.success(function(data){
$rotScope.data=data;
});
})
.factory("UserService", function($http, $rootScope) {
dataFactory.update = function () {
return $http.get("/assets/images/generated.json")
};
return dataFactory;
});
in controller inject dependencies and do like this:
var dataFactory = $rootScope.data;
dataFactory.currentData = null;
UserService.update ()
.success(function(data, status, headers) {
console.log(data);
})
.error(function(data, status, headers, config) {
console.error('error in loading File list');
});

Related

How to make $scope value in another controller update when calling function inside factory?

I'm having 2 controller placed on a same page and using a same factory. All things i want is when a function in controller 1 execute, it will call to the function inside factory then the $scope in controller 2 will be update its value. When page is loaded controller can get the list but after controller 1 call the factory, nothing was changed, no any call to server...
Here is Controller 1:
app.controller('controller1', function ($scope, $http, globalServices) {
$scope.createFuntion = function(){
$http.post(url, $.param(some_object)).then(function(response){
//Handle something ...
globalServices.userList();
});
}});
Here is Controller 2:
app.controller('controller2', function ($scope, $http, globalServices) {
$scope.users = globleServices.userList();});
Here is factory:
app.factory('globalServices', function ($http) {
return{
userList: function(){
var users_data = [];
$http.get(url).then(function (response) {
var res = response.data;
if (res.status === 200) {
angular.forEach(res.data, function (staff) {
users_data.push(staff);
});
} else {
alert('Oops! Somethings went wrong!');
}
});
return users_data;
}
}});
There is a thing in the AngularJs space and JavaScript in general referred to as the dot rule. If you have a property on an object like
service.data
when you assign that to another object
$scope.data = service.data;
It assigns a reference to the object and now if you update the service the controller does not know about the new data.
Using the dot rule you can have an object on the service that holds data objects
service.data = {};
this object should never change reference to a new object and always be the same instance and you can add new properties to it
service.data.userList = response.userList;
Now if you assign the data in the service to the scope
$scope.data = service.data;
and in the template use
<div ng-repeat="user in data.userList">{{ user.name }}</div>
Userlist will be updated when the service updates the userList.
You should never inject $http into controllers, you should only inject services into controllers and have services make http calls. Injecting $scope is an outdated method of doing AngularJs, you are following outdated tutorials and should look into using the controllerAs syntax or use components that wrap the controllerAs syntax with an Angular 2 style of development.
Create an object in your factory that will somehow serve as a state then create a getter for it. Separate your fetch function and getUserList. See the modified code below.
app.factory('globalServices', function ($http) {
var list = {
users_data: []
}
return{
getUserList: getUserList,
fetchUserList: fetchUserList
}
function getUserList() {
return list;
}
function fetchUserList() {
list.users_data = [];
$http.get(url).then(function (response) {
var res = response.data;
if (res.status === 200) {
angular.forEach(res.data, function (staff) {
list.users_data.push(staff);
});
} else {
alert('Oops! Somethings went wrong!');
}
});
}
});
Now in your controller1
app.controller('controller1', function ($scope, $http, globalServices) {
$scope.createFuntion = function(){
$http.post(url, $.param(some_object)).then(function(response){
//Handle something ...
globalServices.fetchUserList();
});
}});
and in your controller2
app.controller('controller2', function ($scope, $http, globalServices) {
$scope.users = globalServices.getUserList();
});
Now your $scope.users listen to every change in your user_data.
Access the array thru $scope.users.users_data

Creating a function around angular $http requests

First project in AngularJS and I started creating my services (factories) that I made modular like this
angular.module('app.services.public', [])
.factory('publicService', ['$http', function publicService($http) {
var results = {};
results.contact = function (name, email, message){
return $http.get();
};
return results;
}]);
That I then call in my main angular app by including it. When I call it, I need to listen for success or error
publicService.contact().success(callback).error(callback)
My question is, I'm going to be doing a lot of API requests through these services and seems to be bad code to listen to the error everytime since 90% of the time it will do the same thing.
How can I create a wrapper around the $http.get or around all factory calls?
So something like
apiCall = function (url, data, successCallback, errorCallback){
$http.get(url,data).success(function(){
successCallback()
}).error(function(){
if(errorCallback()){ errorCallback(); return; }
// or display general error message
})
}
I would recommend against converting promise-based into callback-based APIs. Angular adopted promises and it best to stay with them.
Also, stay away from $http-specific .success/.error and use promise .then/.catch APIs.
How wide do you need to cast your net to handle $http errors?
1) Say, it only applies to your publicService service, then you can "handle" it at the each function:
.factory("publicService", function($http, $q){
function handleError(){
// invokes error handlers
}
return {
onError: function(cb){
// register error handlers
},
doSomethingA: function(){
return $http.get("some/url/A")
.then(function(response){
return response.data;
})
.catch(function(error){
handleError(error);
return $q.reject(error); // still "rethrow" the error
}
},
doSomethingB: function(){
// similar to above
},
// etc...
};
})
Then you could separate request from error handling:
.controller("MainCtrl", function($scope, publicService){
publicService.onError(function(error){
$scope.showError = true; // or something like that
})
})
.controller("FunctionACtrl", function($scope, publicService){
publicService.doSomethingA()
.then(function(data){
$scope.data = data;
});
})
2) Of course, the above, would only apply to request made via publicService. If you want to catch all $http errors, you could implement an $http interceptors. I won't go into detail - there is enough info in documentation and elsewhere - but it would could work like below:
.factory("ErrorService", function(){
return {
onError: function(cb){
// register error handlers
},
broadcastError: function(error){
// invoke error handlers
}
};
})
Then in interceptor, use ErrorService as a dependency:
'responseError': function(rejection) {
ErrorService.broadcastError(rejection);
return $q.reject(rejection);
}
Then you could handle the errors globally:
.controller("MainCtrl", function($scope, ErrorService){
ErrorService.onError(function(error){
$scope.showError = true; // or something like that
})
})
You have the right idea. You can do it easily with a Factory.
myApp.factory(APIService, function(publicService, $http) {
return {
// create methods in here
...
contact: function(cb) {
$http.get(url,data).success(cb).error(function(err){
console.error('oh no!', err);
});
}
};
});
Then you can use it in your controllers.
APIService.contact(function(data){
console.log('response from the api!', data);
});
You can even move your error handler to its own factory as well.
I would suggest an implementation using angular's $q service.
angular.module('app.services.public', [])
.factory('publicService', ['$http', '$q', function publicService($http, $q) {
var results = {};
results.contact = function (name, email, message){
return $q.when($http.get());
};
return results;
}]);
Or rather than use the $q.when(...) method you can use $q.deferred like so:
angular.module('app.services.public', [])
.factory('publicService', ['$http', '$q', function publicService($http, $q) {
var deferred = $q.deferred();
var results = {};
results.contact = function (name, email, message){
$http.get().success(function(data){
deferred.resolve({
// assumes data retried from http request has a title and price attribute
title: data.title,
cost: data.price});
}).error(function(data){
deferred.reject(data);
});
};
return deferred.promise;
}]);

AngularJS: how should I set the params for $http dynamically?

I am very new with AngularJS. Thank you for answer. My code is as follow:
mainModule.controller('MainController', function($scope, $http) {
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'IndexPageEntity'}}).
success(function(data) {
$scope.intro = data[0].IndexPageContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ExhibitionServiceEntity'}}).
success(function(data) {
$scope.exhibit = data[0].ExhibitionServiceContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ShootingServiceEntity'}}).
success(function(data) {
$scope.shooting = data[0].ShootingServiceContent;
});
});
My html file would be:
<div ng-controller="MainController">
<div>{{intro}}</div>
<div>{{exhibit}}</div>
<div>{{shooting}}</div>
</div>
I believe there must be some ways to improve the above code in order to reduce repetition. What I want is to pass entity parameter to the controller on creation.
Using ng-init to pass parameter is discouraged, according to the documentation. Writing custom directive to pass argument to scope does not work since parameters would be overwrittern.
What is the best practice to set params dynamically for use in $http? Thank you.
You should move all the logic to a service and use a directive. I would suggest you to modify your backend to return the same structured data, instead of IndexPageContent, ExhibitionServiceContent, etc. it should be Content or whatever name you want to use. But for now I've added a replace function to get the name of the content from the name of the entity.
mainModule.factory('webService', function($http) {
var apiUrl = 'http://localhost/backend/WebService.php';
function getContent(params) {
var config = {
'params': params
};
return $http.get(apiUrl, config);
};
return {
getContent: function(params) {
return getContent(params)
}
};
});
mainModule.controller('MainController', function($scope, webService) {
var params = {
'entity': $scope.entity
};
var contentName = $scope.entity.replace('Entity', 'Content');
webService.getContent(params).then(function (data) {
$scope.content = data[0][contentName];
});
});
mainModule.directive('EntityContent', function() {
return {
controller: 'MainController',
replace: true,
restrict: 'E',
scope: {
entity: '#entity'
},
template: '<div>{{ content }}</div>'
};
});
<div>
<entity-content entity="IndexPageEntity">
<entity-content entity="ExhibitionServiceEntity">
<entity-content entity="ShootingServiceEntity">
</div>
Create an object data and send the value for the key inside the object at every call.. Also pass the value for key to be set inside the scope..
E.g.
$scope.makeHttpCall = function(data) {
$http.get('http://localhost/backend/WebService.php', {params: data}).
success(function(data) {
$scope[$scope.key] = data[0][$scope.key];
});
};
you can then call this function as
$scope.key = 'IndexPageContent';
data = {
entity : 'yourValueHere'
};
$scope.makeHttpCall(data);
You can set other values as well inside the scope that are dynamic for each request..
I hope this makes sense to you...

AngularJS - API call returning [object object]

I'm trying to call a list of articles from the NPR API. I have a working URL which returns as JSON. However, somewhere in my controller, I'm getting lost getting the object. When I console.log it to test, it returns as [object Object] and nothing else. My service looks like this:
app.factory('nprService', function($resource) {
//call npr api
return $resource('http://api.npr.org/queryid=61&fields=title,byline,text,image,all&output=JSON...
and my controller:
app.controller('ArticleListCtrl', function($scope, nprService) {
//call the service and store as a variable
$scope.article = nprService.get();
});
I've tried using query to get the result, like this, but it's returning a single JSON object so that obviously didn't work.
//call the service and store as a variable
nprService.query(function(data) {
$scope.article = data;
});
Any help would be greatly appreciated. Thanks in advance.
You need to use a promise construct to get at the data. The controller code can be re-written as:
app.controller('ArticleListCtrl', function($scope, nprService) {
//call the service and store as a variable
nprService.get().then(function(result){
$scope.article = result.data;
});
The $resource.get() function returns a promise. It should be used this way:
nprService.get().success(function(data, status, headers, config){
$scope.article = data;
}).error(function(data, status, headers, config){
console.log(status);
});
Using Angular 6 you can try this
myFunctionName(){
this.http.get(`http://www.thesoftdesign.com`)
.map(res => res.json())
.subscribe(data => {
this.data = data;
console.log('data', this.data);
});
}

Angular promise service as global dataservice

I'am not pro in Angular and am still lerning. Hope I get some help here.
I want to build an App with different views. I need to detect the browser and also fetch some data from a server. For this I created a service, where I do this work.
My desire is to use the data of the service all views. How is proper way to store and cache the data so that I can use it in all my Views/Controllers?
Here is what I got so far.
My Service:
.factory('DataService', function($http, $q, $timeout) {
var data = { };
return {
notes: function() {
// This exposed private data
return data;
},
addItem: function(itemname, itemvalue) {
// This is a public function that modifies private data
data[itemname] = itemvalue;
}
getPlatform: function() {
var getPlatformData = function() {
var deferred = $q.defer();
BrowserDetect.init();
deferred.resolve(BrowserDetect.OS);
return deferred.promise;
};
return {
getPlatformData: getPlatformData
};
},
getServerData: function() {
//if(!data.getServerData){
var getData = function() {
var deferred = $q.defer();
$http({
url: 'js/fakeGet.json',
method: 'get',
dataType: 'json',
}).success(function(data) {
data.scanResponse = data;
deferred.resolve(data);
})
return deferred.promise;
};
return {
getData: getData
};
//}
// return data.scanResponse;
}
};
});
My controller:
DataService.getPlatform().getPlatformData().then(function(platform) {
console.log('Another browserDetect request');
$scope.platform = platform;
DataService.addItem("platform", $scope.userPlatform);
});
First of all, as nordyke mentioned in his answer, you'd better split the service to smaller ones.
Second, you're asking for how to caching the data, and since you're using promise, $q.when() is what you need. I will take the getPlatform as an example to get you started:
.factory('DataService', function($http, $q, $timeout) {
var os; // this variable is used to store the result
return {
getPlatform: function() {
var getPlatformData = function() {
if (!os) { // no previous data available, look into other service to fetch the data
var deferred = $q.defer();
BrowserDetect.init();
os = BrowserDetect.OS; // store data
deferred.resolve(os);
return deferred.promise;
}
return $q.when(os); // there is previous data, return it as promise
};
return {
getPlatformData: getPlatformData
};
}
};
});
In this way, OS information is cached, and
DataService.getPlatform().getPlatformData().then(function(platform) {
...
});
will only fetch the platform information once during the life-time of the DataService. You can apply the same idea to getServerData as well to cache the data from the server.
Caching your data in a service singleton is a good approach, and I like your straightforward implementation of it. My only recommendation would be to split up your 3 concerns into separate services.
Browser Detection
Server Requests (which will be split up even more once you have more requests.)
Data Caching

Categories