I am absolutly new in AngularJS and I am studying a tutorial that show how to access to this external weather forecast API: http://openweathermap.org/forecast to retrieve and use weather information.
So, into my application, I have a controller like this:
weatherApp.controller('forecastController', ['$scope', '$resource', '$routeParams', 'cityService', function($scope, $resource, $routeParams, cityService) {
// It contain the city selected in the view:
$scope.city = cityService.city;
$scope.days = $routeParams.days || 2;
$scope.weatherAPI = $resource("http://api.openweathermap.org/data/2.5/forecast/daily",
{ APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK",
cnt: 5
},
{ get: { method: "JSONP" }});
$scope.weatherResult = $scope.weatherAPI.get({ q: $scope.city, cnt: $scope.days });
$scope.convertToFahrenheit = function(degK) {
return Math.round((1.8 * (degK - 273)) + 32);
}
$scope.convertToDate = function(dt) {
return new Date(dt * 1000);
};
}]);
And I am finding some difficulties tho understand how exactly the service is called and the informations are required.
So from what I have understand (but I am absolutly not sure about it so correct me if I do wrong assertion) I have the following situation:
1) Into the controller I inject the $resource service provided by Angular that from what I have understand is an object that I can use for a user friendly RESTful web service interaction.
So by this line:
$scope.city = cityService.city;
I put a city (inserted by the user in the view) into the city property of the $scope of this controller and it will be user as one of the parameter used for the webservice query.
Then I create the weatherAPI property on the $scope object that I think should represent the way to access to this resource
$scope.weatherAPI = $resource("http://api.openweathermap.org/data/2.5/forecast/daily",
{ APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK"
},
{ get: { method: "JSONP" }});
So I think that weatherAPI is a resource object creted by the $resource service (or am I missing something?).
On this I set the web service URL (http://api.openweathermap.org/data/2.5/forecast/daily) and a couple of JSON objects, these:
{
APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK"
},
in which I declare the APPID field that is my personal key to use the key and the callback: "JSON_CALLBACK" field. What exactly represent this callback field?
And this second object:
{ get: { method: "JSONP" }}
Ok, the roole of these 2 objects for me are pretty obscure.
What exactly define? It seems to me that are something related to the security of my application but I am absolutly not sure about it
Finnally it retrieve data from the web service by:
$scope.weatherResult = $scope.weatherAPI.get({ q: $scope.city, cnt: $scope.days });
where I thonk that I am simply passing the 2 parameter (that are something like ?q=value&?cnt=value of a classic HTTPrequest)
So I think that weatherAPI is a resource object creted by the $resource service (or am I missing something?).
No, you're not missing anything. weatherAPI is exactly what you're think it should be.
in which I declare the APPID field that is my personal key to use the key and the callback: "JSON_CALLBACK" field. What exactly represent this callback field?
callback field is just an default GET parameter passed to API on each request. It is set here, so you don't have to pass it each time requesting something from API.
What exactly define? It seems to me that are something related to the security of my application but I am absolutly not sure about it
Third parameter passed into $resource call is an definition of available methods on your API.
In outer object, key is name of method. It is just name on which that method will be available. value is an object of settings for that method, in that case it is only specifying type of your method, which is "JSONP".
JSONP is an special type of request, it can be only an "GET" request, browsers won't allow "POST" here. JSONP must return simple function call, passing one parameter into it, JSON with result data. This function will be called on your JavaScript.
JSONP is just an workaround for making cross site calls.
Related
I am confused why this is happening. I am fairly new with AngularJS/JS so bear with me. I want to create a simple side project that will assist me in learning how to cook, so I am using a recipe database API caled Yummly that will GET recipes with ingredients and other information.
Here's the documentation: https://developer.yummly.com/documentation.
I tried putting api key and ID in the URL as the API said I can. I also tried putting it in the HTTP headers as the API also said I could. I tried both in postman and both worked, but neither work in my code. I get the error:Cannot GET /[object%20Object]. So I am lost. Perhaps this description from the "How to use GET method" from the documentation is the reason?:
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Here's my code. Thanks so much. Much appreciated!
angular.module("app", [])
.controller('AppController', ['$scope', '$http', function ($scope, $http) {
$scope.greeting = 'Hola!';
$scope.getRecipe = function(){
$http.get({
url: 'http://api.yummly.com/v1/api/recipes?q=onion+soup',
method: 'GET',
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
}
}]);
If $http.get is preferred, the code would be:
$http.get('http://api.yummly.com/v1/api/recipes', {
params: {
q: 'onion+soup'
}
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
According to the official document, the syntax of $http.get is:
get(url, [config]);
Shortcut method to perform GET request.
The first parameter of $http.get is a URL string. If an object is passed (as in the question), it would be converted to string, that's why you get the request sent to /[object%20Object] -- [object%20Object] is the toString() result of the config object.
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Don't worry. This will be handled automatically. If the value of q above is 'onion+soup[xxx]', it would be URL-encoded as 'onion%2Bsoup%5Bxxx%5D'.
I have just started exploring AngularJS. Is there any way by which I can extract the original JSON returned from server?
I am trying to get some data using $resource.get from a WebAPI. The returned promise contains some additional properties compared to my original JSON.
When I assign this object (returned from $resource.get) to some of my Javascript controls (like DHTMLX Scheduler), it does not work correctly but if I use $http it returns pure JSON and the control works perfectly.
I am more interested in using $resource as it has been used in the rest of the Angular controllers.
Let's say you have a service that's defined using a resource:
app.factory('LoginService', function($resource, API_URL) {
return $resource(API_URL + '/login.json');
});
Then elsewhere in a controller (for example) you want to use that service:
var loginRequest = LoginService.get({
email: email,
password: password
}).$promise;
loginRequest.then(function onSuccess(response) {
// access data from 'response'
var foo = response.foo;
},
function onFail(response) {
// handle failure
});
The key is that you need to access $resource.get().$promise, not $resource.get().
In my RESTful api one of the resources exposes a GET method that accept json as a parameter named 'query'. This parameter is passed directly to the MongoDB query allowing users to query the database directly using mongo syntax.
The problem I'm having is that the request always looks like this:
?&query=%7B%22source%22:%22incident%22%7D
Where it should look something like this:
?&query={'source': 'incident'}
This is how I am sending the GET request:
var query = {};
if ($scope.sourceFilter) { query.source = $scope.sourceFilter; }
var query = JSON.stringify(query);
$http.get('/api/feedbackEntries', {params: {limit: $scope.limit, query: query}}).success(function(data) { .......
I am doing the same thing on other get requests and I don't get this issue.
Am I doing something wrong here ? Is this to do with the way angular parses params ?
Thanks
Like the $http docs say
params – {Object.<string|Object>} – Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url. If the value is not a string, it will be JSONified.
Latter emphasis is added by me.
So the query property of the object you pass to the params configuration option is an Object. This means is will be JSONified, which means the same as
JSON.stringify(query);
So this
{'source': 'incident'}
Turns to this:
'{"source": "incident"}'
As RFC 1738 states:
... only alphanumerics, the special characters "$-_.+!*'(),", and
reserved characters used for their reserved purposes may be used
unencoded within a URL.
As it happens {, } and " are not on that list and have to be url encoded to be used in a url. In your case %7B corresponds to {, %7D corresponds to } and %22 corresponds to ".
So what is happening is normal and most server software automatically decodes the url query parameters for you, so they will be presented normally. Most likely you'll need to parse it back to JSON somehow!
Hope this helps!
When using RESTFul apis concider using ngResource, ngResource docs
Include it in your module:
yourApp = angular.module('yourApp', ['ngResource'])
Add your service:
yourApp.factory('YourService', ['$resource', function($resource){
return $resource('link/to/your/object', {});
}]);
Add your controller
yourApp.controller('YourController', [$scope,
'YourService', function($scope, YourService) {
$scope.yourData = YourService.get();
$scope.yourData = YourService.query(); //(When obtaining arrays from JSON.)
I've found this is the best way using a RESTFull api.
After some digging I figured this one out. Looking at the request I was making:
$http.get('/api/feedbackEntries',
I saw that the url does not end with a trailing slash. This was the only difference I could see compared with other requests that were working fine. So I added the trailing slash and magically it works. I can't explain why, whether it's something within angular or elsewhere .. but this is how I fixed the problem.
Hope this helps someone in the future.
Below is the code I used to get search result from my server, it works for me sending POST request with JSON params without .
var service = {
getResult: function ({"username": "roman", "gender": "male"}) {
var promise = $http({
url: ServerManager.getServerUrl(),
method: "POST",
data: params,
headers: {
'Content-Type': 'application/json'
}
})
.success(function (data, status, headers, config) {
console.log('getResult success.');
return data;
}).error(function (data, status) {
console.log('getResult error.');
});
return promise;
}
}
return service;
Not the easiest issue to put into a title.
Anyhow, my app is built on nodejs/expressjsand has an API set up for the url:
EDIT: The current code I'm using is:
$scope.updateProduct = $resource('/api/updateProduct/:product/:param/:value',{},{
query: {method:'GET'},
post: {method:'POST'},
save: {method:'PUT', params: {brand: '#brand', param:'#param', value:'#value'}},
remove: {method:'DELETE'}
});
$scope.updateProduct.save({
product : $scope.post._id,
param: 'likes',
value: $scope.user._id
});
At present it calls /api/updateProduct instead of /api/updateProduct/<product>/<param>/<value> like it's supposed to / like it does when I perform $scope.updateProduct.get().
In my console I see (as an example):
PUT /api/updateBrand/Quay%20Eyewear%20Australia/userTags/sunglasses,%20classic 200 30ms - 2.31kb
However, the API isn't actually accessed/nothing happens. Interestingly, if I go to localhost:5000/api/updateBrand/Quay%20Eyewear%20Australia/userTags/sunglasses,%20classic in my browser, it posts the correct data and updates the product in my database, so it's definitely an error with the way the $resource is being called.
As you can see in ngResource docs, $resource receive 3 parameters:
$resource(url[, paramDefaults][, actions]);
You are passing your action as a parameter.
The correct version would be:
$scope.updateProduct = $resource('/api/updateProduct/:product/:param/:value',{}, {'save':{method:'POST'}});
Note that it isn't even necessary, because when you use $resource you already create the default methods:
{
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
First of all, your have defined the save() to have a parameter called "brand", but in your url template and in the call to save(), you are using "product". I guess it's a typo.
You say when you are using browser to visit the url, it works well; but when angular is make a PUT request to the same url, nothing is happening. This indicates that your backend is configure to process only GET requests for this particular url pattern. Therefore, you need to make sure that your backend is accepting PUT requests.
I was struggling with this issue and was able to pass parameters to the resource by doing the equivalent call below (notice the '$' before 'save').
$scope.updateProduct.$save({
product : $scope.post._id,
param: 'likes',
value: $scope.user._id
});
I also did not define a 'save' method in the resource. According to Angular docs:
"Calling these methods (meaning non-GET methods) invoke $http on the url template with the given method, params and headers. When the data is returned from the server then the object is an instance of the resource type and all of the non-GET methods are available with $ prefix. This allows you to easily support CRUD operations (create, read, update, delete) on server-side data."
I'm working on a sort of file-manager application that connects to a RESTFUL file api.
On the angular app, each file and directory is an instance of angular $resource using the file-object property relativePathName as resource id .
js
var File = $resource(url + '/:type/:id', {id: '#relativePathName', type: '#type'}, {…});
The problem is, when updating a file resource, the relativePathName parameter gets url encoded, e.g. / becomes %2F which causes the server to intercept the request before it hits the actual API (I assume the server treats this as a physical address and of returns a 404 response). The API is capable of treating whole url segments as a single param, so basically it'd treat path/to/file as a uri parameter of http://myapp.com/api/files/create/path/to/file and not as a different uri.
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor? If so, how (found nothing on this in the docs)?. What would be a possible solution? passing relativePathName as a parameter instead of declaring it as the resource id (which would require modifying the API)?
Thanks in advance.
Thomas
Using $resource is not the one stop shop for RESTful service calls, it is merely a convenience service for api's that are structured in a certain way. If $resource cannot do what you need, just create your own service using a mix of $resource and $http that that fits the api you are trying to call.
In our app we wanted a different URL for getByName requests, so we override the resource address with the URL parameter of the action getByName like so:
myapp.factory('ListGroup', ['$resource',
function($resource) {
return $resource(
'/API/List/:Id',
{
Id:'#Id',
Name:'#Name'
},
{
getByName: {method: 'GET', url: '/API/List/Group/:Name', isArray: true}
}
);
}
]);
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor?
I'm not sure about inside of the resource constructor but you can use interceptors to programatically alter route urls after they have been generated by $resource.
structure
hooks.config.js
hooks.factory.js
hooks.module.js
hooks.module.js
module.exports = angular
.module("app.hooks", [])
.factory("hooks", require("./hooks.factory.js"))
.config(require("./hooks.config.js"));
hooks.config.js
module.exports = ["$httpProvider", function($httpProvider) {
$httpProvider.interceptors.push("hooks");
}];
hooks.factory.js
module.exports = ["$q", "$location", function($q, $location) {
var basePrefix = "/api/v1";
return {
//invoked on every http request
request: function(request) {
request.url = interpret(request.url);
return $q.when(request);
}
};
function interpret(str) {
//if requesting an html template, don't do anything
if (str.indexOf(".html") > -1)
return str;
//if you're accessing the api, append the base prefix globally here
else
return basePrefix + str;
}
}];