I have an Angular $resource as
var Client = $resource('/api/clients/:id',{id : '#id'}, {
query: { method: 'GET' })
I want to be able to call, say query() or save() via a variable. I.e. I want to say
var method = 'query';
Client[method]()
But this returns an error: Unexpected token [
Why?
It's not an object, which is why you get the error. You can call the method like so:
Client.query();
If you really want to call it via a variable you could map it to an object and call it that way, I wouldn't recommend it though:
var resources = {
query: Client.query,
somethingElse: Client.somethingElse
};
var type = 'query';
resources[type]();
Related
I have an API call that's working great, but I'd like to use it on several controllers so I moved it to it's own service. I'm running into what looks like a classic Scope issue or a misunderstanding of Angular's digest cycle.
'use strict';
myApp.factory('Stuff',['$http', function ($http) {
var Stuff = {};
Stuff.data = {};
Stuff.api = 'http://localhost:8080/api/';
Stuff.getStuff = function() {
var http_stuff_config = {
method: 'GET',
url: Stuff.api + 'stuff/'
};
$http(http_stuff_config).then(function successCallback(response) {
Stuff.data = (response.data);
console.log(Stuff.data); // Returns populated object.
},function errorCallback(response) {
console.log(response.statusText);
});
};
Stuff.getStuff();
console.log(Stuff.data); // Returns empty object.
return Stuff;
}]);
myApp.controller('appController', ['$scope','Stuff',function($scope,Stuff) {
$scope.stuff = Stuff;
console.log($scope.stuff.data); // Returns empty object.
$scope.stuff.getJobs();
console.log($scope.stuff.data); // Returns empty object.
}]);
Here's the big clue. The essential output of above, in order is...
empty object (in service after calling method)
empty object (in controller before calling method)
empty object (in controller after calling method)
populated object (in method execution from service)
populated object (in method execution from controller)
So somewhere between the scope of the getStuff() method and Angular's order of operations, I'm doing something remarkably foolish. Thank you in advance.
You need to add returns on your service, or else the promise will not be returned to the controller. It is not good practice to just store the returns in your services AND NOT return the result to the controller.
This is considered bad practice because, any time you update the data on the service everyone will need to apply $scope.$watch to the service to look for updates. This can be very expensive in large scale apps.
The best Idea is to return the data to the calling controller (if you do not need to cache it, this we can talk about later) and let the controller access it via the promise service.getthing().then(function(result){});
myApp.factory('Stuff',['$http', function ($http) {
var Stuff = {};
Stuff.data = {};
Stuff.api = 'http://localhost:8080/api/';
Stuff.getStuff = function() {
var http_stuff_config = {
method: 'GET',
url: Stuff.api + 'stuff/'
};
return $http(http_stuff_config).then(function successCallback(response) {
return response.data;
console.log(Stuff.data); // Returns populated object.
},function errorCallback(response) {
console.log(response.statusText);
});
};
Stuff.getStuff();
console.log(Stuff.data); // Returns empty object.
return Stuff;
}]);
myApp.controller('appController', ['$scope','Stuff',function($scope,Stuff) {
$scope.stuff = Stuff;
console.log($scope.stuff.data); // Returns empty object.
$scope.stuff.getJobs().then(function(result) {$scope.stuff = result; console.log(result);});
console.log($scope.stuff.data); // Returns empty object.
}]);
I recommend you not to store the result inside the service itself (Stuff.data). Just return your data in the getStuff function and let the appController's scope store the data instead.
remember that $scope.stuff.getJobs() is async
(meaning you can't actually call console.log($scope.stuff.data) on the next line and get the data)
Now if you had a view, with something like <span ng-bind="stuff.data.property"> you could see it work just fine because the view will update by itself when the async function is done. (this is part of angular)
You need to understand that when you run $http, it is making an AJAX request. therefore it will not return an result immediately.
Therefore, if you attempt to use the data coming from $scope.stuff.getJobs(); immediate after invoking this function, you are likely to get nothing.
What you should do is to have your Stuff.getJobs() return a promise, and use promise.then(your own success handler) to correctly handle the returned response.
I have cleaned up your code a little bit. The following is a running sample of your code retrieving data from Yahoo Weather API.
You can play with it on CODEPEN.
html:
<div ng-app="myApp" ng-controller="appController">
<p>{{data}}</p>
</div>
JS:
var myApp = angular.module("myApp", []);
myApp.factory('Stuff',['$http', function ($http) {
var Stuff = {};
Stuff.data = {};
//sample yahoo weather api
Stuff.api = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22nome%2C%20ak%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys';
Stuff.getData = function() {
var http_stuff_config = {
method: 'GET',
url: Stuff.api + 'stuff/'
};
return $http(http_stuff_config);
};
return Stuff;
}]);
myApp.controller('appController', ['$scope','Stuff',function($scope,Stuff) {
$scope.data = "$http service not ran";
var uncompletedAjaxCall = Stuff.getData();
uncompletedAjaxCall.then(
function(responseData){
$scope.data = responseData;
},
function(errorMsg){}
);
}]);
I have this code in Backbone.js where I am trying to create dynamically a URL and call a function from the controller which returns a JSON data.
For some reason when doing the fetch() method at the end the URL in my browser does not change.
I have put a console.log in my getdata() function just to see if the call is happening, and it does. Also i have tried to console.log the new build URL with the parameter at the end, and also this is build successfully.
Any ideas why the URL in not changing in the browser?
Thank you
getUrl: function(celebname){
var urlstr = "http://localhost/Codeigniter/index.php/testcontroller/getdatabasedata?searchvalue="+celebname;
return urlstr;
},
events: {
"click #submitbtn" : "getdata",
},
getdata: function (event) {
var celebname = $('#celebname').val();
this.model.url = this.getUrl(celebname);
this.model.fetch();
},
Backbone will always try to use the collection url, so if you want to fetch or save you should explicitly save the model with a new url.
Try overriding the url in the model like so:
var newUrl = this.getUrl(celebname);
this.model.save({}, { url: newUrl});
Instead of just setting this.model.url = this.getUrl(celebname);
This is my C# WebAPI2 controller, which gets hit:
[HttpGet, Route("bycaseidlist/{idArray}")]
public async Task<IHttpActionResult> GetByCaseIdList([FromUri] List<int> idArray)
This is the call:
var idArray = [4,4,2,4];
var url = baseUrl + 'api/cases/bycaseidlist/' + idArray ;
$http.get(url)
The problem is that the API doesn't get the array, it gets ...this:
In other words an array with one value: 0. Why is this happening? How do I fix it? It seems to be in-line with this answer, but it doesn't work. Should I pass it in the body? I feel like I am missing something obvious.
Get ActionMethods can take objects as arguments. However, the default behavior is to look at the body when the parameter is not a .net primitive. In order to force the action method to use a model binder to read the object data from the request, the parameter can be decorated with the [FromUri] or [ModelBinder] attributes. (Note there are other ways to do this that include doing parameter binding rules but that is probably overkill for what you are trying to accomplish here). Here is an implementation that solves the original problem that you were posing.
<script type="text/javascript">
var ajaxCall = function (myArry) {
var ajaxProperties = {};
ajaxProperties.url = "/api/Mul/Mutiply";
ajaxProperties.type = "Get";
ajaxProperties.data = {};
ajaxProperties.data.numbers = myArry;
ajaxProperties.contentType = "application/json";
console.log(ajaxProperties);
ajaxProperties.success = function (data) {
console.log(data);
}
ajaxProperties.error = function (jqXHR) {
console.log(jqXHR);
};
$.ajax(ajaxProperties);
};
var getData = function (e) {
var myArry = new Array();
myArry.push($('input[name=num1').val());
myArry.push($('input[name=num2').val());
ajaxCall(myArry);
return false;
};
</script>
Controller
[HttpGet]
public IHttpActionResult Multiply([FromUri] int[] numbers)
{
int result = 0;
if(numbers.Length > 0)
{
result = 1;
foreach (int i in numbers)
{
result = result * i;
}
}
return Ok(result);
}
}
I think my mistake was using Get. I might be remembering incorrectly (someone confirm if you know offhand), but Get might not be able to take objects as arguments. Anyway, I changed the method to POST and then changed the param to be sent in the request body, rather than the url. It now works. Here is the working code:
[HttpPost, Route("bycaseidlist")]
public async Task<IHttpActionResult> PostByCaseIdList([FromBody] int[] sqlCaseIdArray)
and the call itself:
function runDbCall(url, sqlCaseIdArray){
return $http({
method: 'POST',
url: url,
data: sqlCaseIdArray
});
}
runDbCall(url, sqlCaseIdArray)
I will come back to this when I figure out if the problem was Get not being able to take objects, but I thought it could in url, just not in body...need to clarify. If someone posts an answer just on that part, I will accept, since that's probably the root of the prob.
I am trying to use POST with $resource object in my app.
I have something like this.
Factory:
angular.module('toyApp').factory('toys', ['$resource', function ($resource) {
return $resource('/api/v1/toy/:id/condition/:condid',
{ id: '#id',
condid: '#condid' }
);
}]);
Controller:
$scope.addNew = function() {
//how do I pass id and condid below?
toys.save({'name': 'my first toy'});
})
The above code will pass url like
/api/v1/toy/condition/
I need to send the request url like
/api/v1/toy/6666/condition/abd with parame {'name': 'my first toy'}
How do I do it?
Thanks for the help!
It's very clearly described in the API reference:
https://docs.angularjs.org/api/ngResource/service/$resource
What $resource(url) returns is a class object. If you want to create a new instance and save it, you'll call the $save method on the instance:
var Toy = $resource('/api/v1/toy/:id/condition/:condid',
{id: '#id', condid: '#condid'});
var toy = new Toy({'id': 123, 'condid': 456, 'name': 'my first toy'});
toy.$save();
But if you want to call an object creation API, you'll have to add a custom method to your resource:
var Toy = $resource('/api/v1/toy/:id/condition/:condid',
{id: '#id', condid: '#condid'},
{createToy: {method: 'POST', url: '/create-toy'}});
Toy.createToy({name: 'Slingshot'});
var newToy = new Toys({id: '6666', condid: 'abd'});
newToy.name = 'my first toy';
newToy.$save();
Try this
$scope.addNew = function() {
toys.save({'id': 'foo', 'condid': 'bar'});
})
You are correct in extrapolating $http controller logic to a service/factory.
Create a method to set the object that you will send with the HTTP POST request. Another method to set the url may also be created. The controller will then call these methods before saving to set the url and object to be used for the HTTP call. A dynamic url may be specified in the controller (with unique id and other fields as necessary) and sent to the service.
Service code:
var dataObj = [];
var myUrl = "";
//called from controller to pass an object for POST
function setDataObj(_dataObj) {
return dataObj = _dataObj;
};
function setUrl(_url) {
return myUrl = _url;
}
function saveToy() {
//if sending a different type of obj, like string,
//add "headers: { 'Content-Type': <type> }" to http(method, url, header)
$http({ method: 'POST', url: myUrl })
.then(function(data) {
return data;
})
.catch(function(error) {
$log.error("http.post for saveToy() failed!"); //if log is added to service
});
};
Controller code:
$scope.id = 574; //or set somewhere else
$scope.condid = 'abd';
$scope.objectYouWantToSend = [{"toyName": "Teddy"}];
// to obtain dynamic url for /api/v1/toy/:id/condition/:condid
$scope.url = '/api/v1/toy/' + $scope.id + '/condition/' + $scope.condid;
$scope.addNewToy = function() {
toyService.setUrl(url); //set the url
toysService.setDataObj($scope.objectYouWantToSend); //set the dataObj
toysService.saveToy(); //call the post method;
};
John Papa's AngularJS style guide is well put together and covers scenarios in multiple formats. Below is a link to the data-service factory section:
https://github.com/johnpapa/angular-styleguide#separate-data-calls
i'm trying to make a ajax update in prototype with some values from a multirecordselect that sends a requests like.
Parameters: {"action"=>"use_campaign", "campaigns"=> ["27929","27932"] , "advertiser_id"=>"", "controller"=>"admin/reporting", "ad_id"=>""}
as you can see the request sends the "campaigns" elements as an array of values, i'm trying to do the same with this js code over prototype 7.
// get the campaigns
var campaign_ids = {};
var campaigns = $('filter_form').getInputs("hidden","report[campaigns][]");
campaigns.each( function(field) {
campaign_ids.push(field.value);
});
new Ajax.Updater('ad_filter', '/admin/reporting/use_campaign', {
method : 'get',
asynchronous : true,
evalScripts : true,
parameters : {
'advertiser_id' : $('filter_form')['report[advertiser_id]'].value,
'ad_id' : $('filter_form')['report[ad_id]'].value,
'campaigns' : campaign_ids
}
});
the campaigns_ids is getting the correct info as an array like:
[ "27929", "27932" ]
but seems that prototype ajax update is sending a request like:
http://my_domain/admin/reporting/use_campaign?ad_id=&advertiser_id=&campaigns=27929&campaigns=27932
what sends parameters like:
Parameters: {"action"=>"use_campaign", "campaigns"=> "27929" , "advertiser_id"=>"", "controller"=>"admin/reporting", "ad_id"=>""}
I also tryed with
Object.toJSON(campaign_ids)
but i only get an escaped string like
Parameters: {"action"=>"use_campaign", "campaigns"=>"[\"27929\",\"27932\"]" , "advertiser_id"=>"", "controller"=>"admin/reporting", "ad_id"=>""}
There is anyway to do this as I wish?
Thanks for all.
It looks like you use PHP as a back-end framework.
To make sure PHP understands array-like GET parameters, you need to add a [] to the parameter name:
parameters : {
//...
'campaigns[]' : campaign_ids
}