How to pass param from controller to service in AngularJs - javascript

I'm currently working on a project to help me better understand angularjs! I am currently stuck on how to pass a parameter from the controller to service.
In my program, I have created a function called "GetForecastByLocation" when a user types in an input clicks on a button. From there I want to take their input and then pass it to the http call in service.js.
Originally, $http.get was in a long giant string of the API url, but I googled around and it seems that I'm supposed to use parameters when trying to change a portion of the string. As of right now, I know parameter is hardcoded to a specific city, but I want to take new input and pass the value of vm.city to the $http.get call.
If any one can help I would greatly appreciate it. Thank you!
controller.js
var app = angular.module('weatherApp.controllers', [])
app.controller('weatherCtrl', ['$scope','Data',
function($scope, Data) {
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);},
Data.getApps(city)
.then(function(data)){
//doing a bunch of things like converting units, etc
},
function(res){
if(res.status === 500) {
// server error, alert user somehow
} else {
// probably deal with these errors differently
}
}); // end of function
}]) // end of controller
service.js
.factory('Data', function($http, $q) {
var data = [],
lastRequestFailed = true,
promise;
return {
getApps: function() {
if(!promise || lastRequestFailed) {
promise = $http.get('http://api.openweathermap.org/data/2.5/weather?',{
params: {
q: Tokyo,
}
})
.then(function(res) {
lastRequestFailed = false;
data = res.data;
return data;
}, function(res) {
return $q.reject(res);
});
}
return promise;
}
}
});

Passing arguments to a factory method is no different than passing arguments to a plain old function.
First, set up getApps to accept a parameter:
.factory('Data', function($http, $q){
// ...
return {
getApps: function(city){
promise = $http.get(URL, {
params: {q: city}
}).then( /* ... */ );
// ...
return promise;
}
};
});
Then pass it your argument:
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);
}

It's just like setting a value to a function's context variable.
Services.js
Simple example of a service.
.factory('RouteService', function() {
var route = {}; // $Object
var setRoute_ = function(obj)
{
return route = obj;
};
var getRoute_ = function()
{
if(typeof route == 'string')
{
return JSON.parse(route);
}
return null;
};
return {
setRoute: setRoute_,
getRoute: getRoute_
};
})
Controllers.js
Simple example of Service usage:
.controller('RoutesCtrl', function ($scope, RouteService) {
// This is only the set part.
var route = {
'some_key': 'some_value'
};
RouteService.setRoute(route);
})

Related

Change data in Service

I have a service that grabs JSON from a URL and I would like to alter that data but I cant seem to do it. Right now I change this in the controller but this seems messy with the scope not reaching places I would like.
//SERVICE
app.service("servers", function ($http, $q)
{
// Grab json
var deferred = $q.defer();
$http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd').then(function (data)
{
deferred.resolve(data);
});
this.getItems = function ()
{
return deferred.promise;
}
})
// CONTROLLER
.controller("AppCtrl", function ($scope, servers, geoIP) {
var promise = servers.getItems();
promise.then(function (data)
{
$scope.items = data.data.items;
});
$scope.getSelectedItem = function() {
return servers.selectedItem;
}
$scope.selectServer = function(item)
{
servers.selectedItem = item;
servers.selectedItem.refactored_match_state = lowerCamelCaseUnderscoreRefactor(servers.selectedItem.session.attributes.match_state);
}
//COMPONENT/TEMPLATES
//dbServerTable
<tr data-ng-repeat="item in items | filter:search | orderBy:'name'" data-ng-click="selectServer(item)">
<td>{{item.display_name}}</td>
</tr>
//dbServerInfoSidebar
<li>{{getSelectedItem().refactored_match_state}}</li>
Could anyone show me with code how to alter data in a service that can be used anywhere by any controller that can access that service
The way the service has been coded is an anti-pattern and should be avoided. Refer this link.
Change your service like below and make modifications to the data before you return the data within the .then function.
app.service("servers", function ($http)
{
this.getItems = function ()
{
return $http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(function (data)
{
// **** Modify data here ****
return data;
});
}
})
You can use transformResponse property of $http
service;
You can modify your data before resolving the promise
deferred.resolve(data);.
Are you trying to do something like:
app.service("servers", function ($http, $q)
{
this.parseData = data => {
//modify data;
return data;
};
this._request = $http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(this.parseData);
this.getItems = () => this._request;
});
You don't need to use deferred at all. It's unnecessary. $http returns a promise for you. If you want to alter any data, you just need to chain after the request and return the modified data in the chained method.
app.service('services',['$q','$http','$rootScope',function($q,$http,$rootScope){
var obj ={};
obj.getData =function(x){
var defer = $q.defer();
$http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(function(response){
defer.resolve(response);
},function(error){
defer.reject(error);
});
return defer.promise;
}
return obj;
}])
app.controller('ctrl',function($scope,services){
$scope.getItems = function(){
services.getData()
.then(function(response){
$scope.Items=response.data;
},function(error){
console.log(error);
});
}
})

How to make a simulated sync AJAX call in AngularJS?

I have this script in my app.js:
app.run(['$http', '$location', 'myAppConfig', function ($http, $location, myAppConfig) {
if (myAppConfig.webAPIPath.main == '') {
var getconfigDone = false;
$http.get('fileHandler.ashx?action=getconfig')
.then(function (result) {
if (JSON.parse(result.data.Data).APIURL !== undefined && JSON.parse(result.data.Data).APIURL != '') {
var apiURL = JSON.parse(result.data.Data).APIURL;
if (apiURL.lastIndexOf('/') + 1 == apiURL.length) {
apiURL = apiURL.substring(0, apiURL.lastIndexOf('/'))
}
myAppConfig.webAPIPath.main = apiURL + "/";
myAppConfig.webAPIPath.account = myAppConfig.webAPIPath.main + '/api/OnlineApplicationPortal/v1/Account/';
myAppConfig.webAPIPath.dashboard = myAppConfig.webAPIPath.main + '/OnlineApplicationPortal/v1/Dashboard/';
}
else {
$location.path('Action/Welcome/apiUrlError');
}
//debugger
getconfigDone = true;
}, function (response) { debugger }
);
}
}]);
Also I have got this factory object which uses the myAppConfig in app.js:
(function () {
angular
.module('app.data')
.factory('accountDS', ['$http', '$routeParams', 'myAppConfig', function ($http, $routeParams, myAppConfig) {
var pathPrefix = myAppConfig.webAPIPath.account;
var createAccount = function (account, email) {
var OnlineApplicationPortalModel = {
Name: account.firstName,
Surname: account.lastName,
Email: email,
Password: account.password
};
return $http.post(pathPrefix + 'CreateAccount', OnlineApplicationPortalModel)
.then(function (response) {
return response;
});
};
var confirmEmail = function () {
var data = {
guid: $routeParams.guid
};
return $http.post(pathPrefix + 'ConfirmEmail', data)
.then(function (response) {
return response;
});
}
return {
createAccount: createAccount,
confirmEmail: confirmEmail
};
}]);
})();
The service object needs to use myAppConfig.webAPIPath.account which is resolved in the function in app.js run function. Now the problem is sometimes the browser reaches the service code sooner than than the AJAX call is returned, a race condition. I know that it is not possible in AngularJS to make a sync AJAX call. So how can I solve this?
If I correctly understand you, you want to myAppConfig.webAPIPath.account resolve this value to use it later in your code, but ajax call which provides you value for this variable is not always called before assignment. I think you could use https://docs.angularjs.org/api/ng/service/$q to solve your problem. Your code in myAppConfig should be inside function, so you can call it inside your factory and return deferred object, which then when your .account variable is set should call code from accountDS factory.

Angular $resource

I am using the following pattern for my REST API, but vm.listing in my controller is always undefined? Probably my pattern is not right? Is there a different pattern to use here? I don't want to call the .get(..) in my controller code.
.factory("listingsResource", ["$resource", "$q", 'appSettings',
function ($resource, $q, appSettings) {
return $resource(appSettings.serverPath + "api/Listings/:id")
}]);
.factory("editService",
var _listing;
var _getListing = function (listingId) {
_listing = listingsResource.get({
id: listingId
});
}
return {
listing: _listing,
getListing: _getListing
};
Controller Code:
createService.getListing(listingId);
vm.listing = createService.listing;
The problem is that when you call listingsResource.get() it returns a promise. Not the data response.
You have to pass the get() a success callback and then set the listing variable inside this callback. I would do something like this:
.service("listingsService", ["$resource", "$q", 'appSettings',
function ($resource, $q, appSettings) {
var _this = this;
_this.listing = {};
var listingResource = $resource(appSettings.serverPath + "api/Listings/:id");
this.getListing = function(listingId){
listingResource.get({id: listingId},
function (data) {
// Success callback
// Set listing keys-value pairs
// do not do: _this.listing = data
_this.listing.id = data.id;
_this.listing.title = data.title;
},
function(err) {
// error callback
console.log(err);
}
)
}
}
])
This works fine with a factory aswell if you prefer. Then in the controller:
listingService.getListing(listingId);
vm.listing = listingService.listing;

How to query and extract from server response in Angular

I want to create a find method that loops through an array returned by the $resource service in Angular.
If I have a service like so:
'use strict';
angular.module('adminApp').factory('ProductType', function($resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName){
var types = this.query(),
typeObject = {},
self = this;
for(type in types) {
var result = types[type],
resultName = self.normalizeName(result.name),
if(typeName === resultName) {
typeObject = result;
}
}
return typeObject;
};
return ProductType;
});
I tried wrapping it all in a function and returning the function thinking it had something to do with it being async and I also tried nesting a callback in the query method but that just allowed me to modify the response and not actually return anything differently.
When I try and set the return value to $scope in the controller I get a blank object
The this.query() method would return an array which might not be filled until the this.query() method has got its results back from the server. You will need to do something like this to wait until the call to the server has completed. As this is sort of async you will need to return a promise from this method that is resolved when the initial query has completed and you have searched the results.
'use strict';
angular.module('adminApp').factory('ProductType', [
'$q',
'$resource',
function($q, $resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName) {
var defer = $q.defer(),
types = this.query(),
self = this;
types.$promise.then(function () {
var result,
resultName,
typeObject,
type;
for(type in types) {
result = types[type];
resultName = self.normalizeName(result.name);
if(typeName === resultName) {
typeObject = result;
break;
}
}
defer.resolve(typeObject);
}, function (err) {
// the called failed
defer.reject(err);
})
return defer.promise;
};
return ProductType;
}]);
Taken from the angular docs https://docs.angularjs.org/api/ngResource/service/$resource
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data.

Watching changed in the service from the controller

I am trying to listen to changes in my injected service (self-updating) in the controller. In the below example you'll find two $watch cases - one that works but I don't know exactly why and one that was obvious to me, yet doesn't work. Is the second example the right way to do it? Isn't that code duplication? What is the right way to do it?
Service:
app.factory("StatsService", [
'$timeout', 'MockDataService',
function ($timeout, MockDataService) {
var service, timeout;
timeout = 5000;
service = {
fetch: function () {
// Getting sample data, irrelevant, however this is what updates the data
return this.data = MockDataService.shuffle();
},
grab: function () {
this.fetch();
return this.update();
},
update: function () {
var _this = this;
return $timeout(function () {
return _this.grab();
}, timeout);
}
};
service.grab();
return service;
}
]);
Controller:
app.controller("StatsController", [
'$scope', 'StatsService',
function ($scope, StatsService) {
var chart;
$scope.stats = StatsService;
$scope.test = function (newValue) {
if (arguments.length === 0) {
return StatsService.data;
}
return StatsService.data = newValue;
};
// This doesn't work
$scope.$watch('stats', function (stats) {
return console.log('meh');
});
// This works, don't know why
$scope.$watch('test()', function (stats) {
return console.log('changed');
});
}
]);
See the third parameter for $watch: objectEquality
Compare object for equality rather than for reference.
However if you're only interested in watching the returned data, then you should do:
$scope.$watch('stats.data', function (stats) {
return console.log('meh');
});
You could use $rootScope events. For example inside the service you could dispatch an event with $rootScope.$broadcast("somethingFetched", data) and catch it in the controller $scope.$on("somethingFetched", function(event, data) { $scope.data = data }).
More details you could find in the documentation http://docs.angularjs.org/api/ng.$rootScope.Scope

Categories