I need to GET data from a rest API, with the product id part of the url (and not as query parameter).
The factory:
.factory('Products', ['$resource',
function($resource) {
return $resource('products/:productId', {
productId: '#id'
}, {
query: {
isArray: false
},
update: {
method: 'PUT'
}
});
}
])
The controller:
$scope.getProduct = function(id, from) {
$scope.product = Products.get({ id: id }, function(){
console.log($scope.product);
});
}
My url is constructed like:
/products?id=5426ced88b49d2e402402205
instead of:
/products/5426ced88b49d2e402402205
Any ideas why?
When you call Products.get() in the controller, you are not using the correct parameter name (you need to use "productId" instead of "id" based on your definition of the $resource). Try calling it like this instead:
Products.get({ productId: id })
Here is a snippet from the documentation for $resource which explains how it works:
Each key value in the parameter object is first bound to url template if present and then any excess keys are appended to the url search query after the ?.
In your case, it's not finding "id" as a parameter in the URL, so it adds that to the query string.
Related
My controller Action method looks like the below:
[HttpGet]
[Route("ShowModal")]
public Task<IActionResult> GetDetails(int id, string name, IEnumerable<Employee> employees)
{
//create a model
//Some business logic codes
return PartialView("_Partial.cshtml", model);
}
I need to call the above Action Method from jQuery's $.get() method on a button click, capture the partial view returned as HTML, and show it in a Bootstrap popup.
I am not able to pass the IEnumerable<Employee> from the jQuery method, it is always null, whatever I try.
Below is the JS code:
<a class="btn btn-primary" onclick="ShowModal();" data-keyboard="true" data-toggle="modal">ShowModal</a>
<div class="modal fade" id="divShowModalDialog" role="dialog" tabindex="-1">
<div class="modal-body" id="divShowModalBody">
</div>
</div>
function ShowModal()
{
var list = [{ Id: 101, Gender: 'MALE' }, { Id: 102, Gender: 'FEMALE' }];
list = JSON.stringify(list);
var data = { 'id': 999, 'name': 'JAMES', 'employees': list };
$.get('/Area1/Controller1/ShowModal', data)
.done(function (response) {
if (response != undefined) {
$('#divShowModalBody').html(response);
$('#divShowModalDialog').modal(
{
backdrop: 'static',
keyboard: true,
});
}
})
.fail(function (xhr) {
console.log(xhr);
})
}
I get the id and name parameter in the Action method, but the list is always empty. I have tried after removing JSON.stringify() as well, but it doesn't work.
I know I'm missing a trivial thing, please help.
First, you should be using [HttpPost] on your controller action and not [HttpGet], and of course you'll need to use post from jQuery which is using $.post() and that is because 'POST' is the correct - but not the only - HTTP verb to actually post data to the server side.
Second, you shouldn't stringify your employees list before you put it in your data javascript object that you are sending.
so, list = JSON.stringify(list); and just straight away go
var data = { 'id': 999, 'name': 'JAMES', 'employees': list };
You also might need to provide the dataType using $.post(url,data,onsucess,dataType) check documentation in the link above.
Last, on your action method remove IEnumerable<T> and replace it with a concrete collection type like List<T> because the JSON serializer will need to know which type of collection to instantiate at binding time.
Actually you can achieve it without changing it to POST by using $.ajax()
Use a dictionary object instead of IEnumerable in action method
public ActionResult GetDetails(int id, string name, Dictionary<int,string> employees)
{
And then in the script
var list = [{ Id: 101, Gender: 'MALE' }, { Id: 102, Gender: 'FEMALE' }];
var data = { id: 999, name: 'JAMES', employees: list };
debugger;
$.ajax({
url: '/Home/GetDetails',
type: "GET",
data :data,
contentType: "application/json",
dataType: "json"
});
I was replying to your comment but decided it would be easier to demonstrate my point as an answer.
To answer your question, no I am not sure. But thats why I asked you to try it first, it seems logical as you are passing a list and not IEnumerable to your function.
Also, depending on what your Employee class looks like, you should try this: (you need a constructor in your Employee class for this)
List<Employee> list = new List<Employee>();
list.Add(new Employee(101, 'MALE'));
list.Add(new Employee(102, 'FEMALE'));
var data = { 'id': 999, 'name': 'JAMES', 'employees': list };
...
Update
I realize why I'm wrong, I kept thinking in C# terms. Json.stringify() returns a json style string (which C# just sees as a string), so your public Task GetDetails(int id, string name, IEnumerable employees) should be public Task GetDetails(int id, string name, string employees) and then in C#, you need to parse the JSON string. A helpful link:
How can I parse JSON with C#?
I'll outline what I've set up and what I'd like to work:
I have a list of items in my template like this:
<ul>
<li ng-repeat="user in users">
<a ui-sref="user({slug: user.slug, id: user.id})">
{{user.name}}
</a>
</li>
</ul>
I have a state provider looking like this:
.state('user', {
url: '/:slug',
controller: 'UserCtrl',
templateUrl: 'app/user/userProfile.html',
resolve: {
user: function($stateParams, UserService) {
return UserService.get($stateParams.id);
}
}
});
Now I understand that this isn't working because the id parameter is not defined in the url and so it isn't passed to the $stateParams. I have seen similar questions answered with suggestions of using something like this:
resolve: {
user: function($stateParams, UserService) {
var userId = UserService.getIdFromSlug($stateParams.slug);
return UserService.get(userId);
}
}
But the getIdFromSlug method is either going to require another API call or when I get the list, creating a mapping object in the service like { slug: id }. Both don't seem like very elegant solutions, especially since I already have the id.
Am I missing something? Is there no other way to pass parameters through to the state provider? (I am fairly new to Angular).
.state('user', {
url: '/:slug',
params: { id: { value: -1 } },
I have used the above so the ids where not passed in the url.
You can pass both slug and id , but than you have to code both in the url
url: '/:slug/:id'
like this you can pass both
You can add id in the url like
url: '/:slug/:id'
Or treat it as the query string like below
url: '/:slug&id
For my app, I've created a service for Address, which allows me to manipulate Address objects for any given user. Aside from my standard CRUD functions, I need to have one function to list any address for a specified Parse.User.
services.js
.factory('Address',['$http', 'PARSE_CREDENTIALS', function ($http,PARSE_CREDENTIALS) {
return {
// constrain to User ID
getAll: function(userId) {
return $http.get('https://api.parse.com/1/classes/Address', {
headers: {
'X-Parse-Application-Id': PARSE_CREDENTIALS.APP_ID,
'X-Parse-REST-API-Key': PARSE_CREDENTIALS.REST_API_KEY,
'Content-Type' : 'application/json'
},
params: { "userId": userId }
});
},
// ...get(), edit(), add(), delete()
controllers.js
.controller('AddrCtrl', ['$scope', '$state', '$stateParams', '$rootScope', 'Address',
function($scope, $state, $stateParams, $rootScope, Address) {
Address.getAll($rootScope.user.id)
.success( function(data) {
console.log(data);
$scope.addresses = data.results;
})
}]);
In my Angular template, the view does return Address objects. But it returns all the Address objects when it should only be returning the Address objects with a corresponding userId. To clarify, the Address class has a userId pointer column. Each address only has one User.
Here is the log message that AddrCtrl returns in the console:
Object {results: Array[2]}
results: Array[2]
0: Object
firstName: "(test)"
lastName: "test"
// more unrelated properties
objectId: "yUEuFjLlzs"
updatedAt: "2014-12-02T20:17:55.608Z"
userId: Object
__type: "Pointer"
className: "_User"
objectId: "q1KADkp4i1"
I'm assuming that the issue lies somewhere in my $http.get() function. Ultimately, my questions is this: why does my params option not constrain my data.results to just the Address objects associated with one Parse.User?
Answer I am not looking for:
Return all Address objects and only save the ones matching Parse.User.current().id into $scope.
You need to use where clause to perform the query.
If the data type of userId is Pointer, you should write as following:
{"where": JSON.stringify({
"userId": {"__type":"Pointer","className":"_User","objectId":"userId"}}
)}
I am working on an ASP.NET MVC 4 app. This app has a controller with an action that looks like the following:
public class MyController : System.Web.Http.ApiController
{
[ResponseType(typeof(IEnumerable<MyItem>))]
public IHttpActionResult Get(string id, string filter)
{
IEnumerable<MyItem> results = MyItem.GetAll();
List<MyItem> temp = results.ToList<MyItem>();
var filtered = temp.Where(r => r.Name.Contains(filter);
return Ok(filtered);
}
}
I am calling this action using the following JavaScript, which relies on the Select2 library:
$('#mySelect').select2({
placeholder: 'Search here',
minimumInputLength: 2,
ajax: {
url: '/api/my',
dataType: 'json',
quietMillis: 150,
data: function (term, page) {
return {
id: '123',
filter: term
};
},
results: function (data, page) {
return { results: data };
}
}
});
This code successfully reaches the controller action. However, when I look at id and filter in the watch window, I see the following errors:
The name 'id' does not exist in the current context
The name 'filter' does not exist in the current context
What am I doing wrong? How do I call the MVC action from my JavaScript?
Thanks!
You're not passing actual data as the parameters, you're passing a function:
data: function (term, page) {
return {
id: '123',
filter: term
};
}
Unless something invokes that function, the result will never be evaluated. Generally one would just pass data by itself:
data: {
id: '123',
filter: term
}
If, however, in your code there's a particular reason (not shown in the example) to use a function, you'll want to evaluate that function in order for the resulting value to be set as the data:
data: (function (term, page) {
return {
id: '123',
filter: term
};
})()
However, these errors also imply a second problem, probably related to however you're trying to debug this:
The name 'id' does not exist in the current context
The name 'filter' does not exist in the current context
Even if no values were being passed, id and filter still exist in the scope of the action method. They may be null or empty strings, but the variables exist. It's not clear where you're seeing that error, but it's definitely not in the scope of the action method.
I like the way the query() method returns an array of resources, which can be saved to the server again.
I am trying to use Angular against the Drupal RestWS module, which returns an object with several "meta" properties and a property called list where the actual data are stored. Is there please a way of telling the resource to take that array instead ?
Example : GET author.json returns :
first: "http://dgh/author?page=0"
last: "http://dgh/author?page=0"
list: [{id:1, type:author, uid:{uri:http://dgh/user/1, id:1, resource:user}, created:1367770006,…},…]
self: "http://dgh/author"
With the latest Angular version (1.1.2 or later), you can configure the resource with a transformResponse:
var MyResource = $resource(
'/author.js',
{},
{
'get': {
method: 'GET',
transformResponse: function (data) {return angular.fromJson(data).list},
isArray: true //since your list property is an array
}
}
);