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#?
Related
I know this is a popular topic and I've tried all of the solutions I could find already out there to no avail. I've used all the solutions included for this questions: Pass a List from javascript to controller. I've simplified my test to ridiculously simple. I get into the controller but my controller input param is {int[0]}. I confirmed my array data looks good in the JavaScript and ajax call.
Can anyone please tell me what I am missing?
JavaScript Code
var selectedIds = [];
selectedIds.push(565);
selectedIds.push(573);
selectedIds.push(571);
// selectedIds = [565, 573, 571]
$.ajax({
type: "POST",
traditional: true,
dataType: "json",
data: { "ids": JSON.stringify(selectedIds) },
//data: { "ids": selectedIds},
//data: { ids: selectedIds},
url: "api/services/getbuildingsbyid",
success: function (result) {
return result;
}
});
Controller Code
[HttpPost]
public bool GetBuildingsById(int[] ids)
{
var lookAtIds = ids; // {int[0]}
return true;
}
By using JSON.stringify(selectedIds) you are turning an array of int into a string representation of said array.
The AJAX POST ends up like so:
{ "ids": "[565, 573, 571]" }
instead of
{ "ids": [565, 573, 571] }
If instead you just use the variable for the data, that should resolve the issue:
data: { "ids": selectedIds },
Edit-
Working backwards, by setting a variable to the data we can see the following:
var data = {"ids": JSON.stringify(selectedIds)}
typeof(data["ids"]) // string
data["ids"][0] // '['
data = {"ids": selectedIds}
typeof(data["ids"]) // object
data["ids"][0] // 565
I use:
public ActionResult GetBuildingsById(String selectedIds)
{
// this line convert the json to a list of your type, exactly what you want.
IList<int> Ids = new JavaScriptSerializer().Deserialize<IList<int>>(selectedIds);
return Content(ids[0].ToString());
}
This will take you selectedIds = [565, 573, 571]
and create the list of your type List
First, let me just say that there are many similar questions to this already posted here, but I've tried all of the solutions and have had no joy.
I have an MVC ApiController Action whose signature is:
public void Post([FromBody] Employee employee)
I've tried with or without [FromBody].
My Employee class has 5 members of base types, int, string, string, string, double
I am calling the Action from a separate domain (both on my localhost using Cors), using jQuery Ajax as follows:
$.ajax({
url: "http://localhost:55555/api/Employees",
type: "POST",
headers: {
"Authorization": "Bearer " + sessionStorage.getItem("accessToken")
},
data: {
Id: 100,
FirstName: "Fred",
LastName: "Blogs",
Gender: "Male",
Salary: 100000
},
// ...
This works absolutely fine. There is no problem with the Authorisation. The Employee object on the Controller is populated with this data. But what I want to do is send the data in a more "structured' way. So my data would look something like this:
data: {
employee: employee
},
I have tried everything I can think of to format the employee. Specified all combinations of:
contentType: "application/json; charset=utf-8",
dataType: "json",
Combined with different ways to "stringify" my employee object:
var employee = {
Id: 100,
FirstName: "Fred",
LastName: "Blogs",
Gender: "Male",
Salary: 100000
};
employee = JSON.stringify({ "employee": employee });
employee = JSON.stringify(employee);
In case this is related (as a more simple example) I also have this method on my ApiController:
public void Delete([FromBody] int employeeId)
Again, I am unable to pass the employeeId in the "desired" way.
I can't even get:
data: {
employeeId: 100
},
To work!?!
I can get the Delete method to work is I change it to [FromUri] and and specify the "?employeeId=100" in the calling Url.
I was hoping that I could be a bit more consistent across the different call and always pass an 'object' in JSON format.
Does anyone have any ideas that may help me progress?
Thanks.
More information on the Delete call.
If I use Postman, remove requirement for Authentication, specify Content-Type "application/json"
in the header and employeeId = 100 in the body, Postman returns:
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'employeeId' of non-nullable type 'System.Int32' for method 'Void Delete(Int32)' in 'WebApi.Controllers.EmployeesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
I don't think that "DELETE" passes a body like a "POST" so the [FromBody] wont work if you are calling the ajax call with the type: "Delete".
As for the employee post call, I have seen similar issues. The only work around I could find was to make a wrapper class that wraps the employee.
public class EmployeeWrapper {
public Employee employee {get; set;}
}
Then you have EmployeeWrapper be the parameter for the Post method.
public void Post([FromBody] EmployeeWrapper employeeWrapper) {
Employee employee = employeeWrapper.employee;
...
This lets you call with the ajax data:
data: {
employee: employee
},
Change data to this:
data: JSON.stringify({ employee : employee })
The following combination finally fixed my issue.
var employee = {
Id: 100,
FirstName: "Fred",
LastName: "Blogs",
Gender: "Male",
Salary: 100000
};
employee = JSON.stringify(employee);
//...
contentType: "application/json",
data: employee,
//...
One of the reason's I did not arrive at this earlier, was that I was tending to surround the data: { ... } with curly braces. This was causing it to fail.
Also managed to get the DELETE working in the Body of message in exactly the same way.
I am trying to access my Products collection in Minimongo in the html page. When I am in my browser console, I am able to type Products.findOne(); and it will return a product.
However, when I try to return a product from my template helper, I get undefined. Thoughts anyone?
Template.Tires.onRendered(function() {
console.log(Products.findOne());
//after I return a product item, I need to modify its properties manually after it has loaded into the client
});
Simple answer:
Do whatever modification you need to do on the collection within the helper function and then return a JS object. For instance if you collection looks something like this:
SomeColleciton
_id
type: String
birthday:
type: Date
firstname:
type: String
lastname:
type: String
timezone:
type: Integer
you can do the following to transform it
Template.Tires.helpers({
user: function(userId) {
var u = SomeCollection.findOne(userId);
var age = calcAge(u.birthday);
var ifworkinghour = calcifworkinghour(u.timezone);
return {name: u.firstname + ' ' + u.lastname, age: age, workinghour: ifworkinghour}
});
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.
Below is the current code structure I have in place for a collection that I have manually constructed. I have a json file on my server which I am now trying to load in and basically remove the manual one and construct a collection based on that data. Was wondering what would I possibly need to change below to my code to help accommodate this.
var Game = Backbone.Model.extend({
defaults: {
name: 'John Doe',
age: 30,
occupation: 'worker'
}
});
var GameCollection = Backbone.Collection.extend({
model: Game,
url: 'path/to/json',
parse: function(response) {
return response;
}
});
var GamesView = Backbone.View.extend({
tagName: 'ul',
render: function() {
//filter through all items in a collection
this.collection.each(function(game){
var gameView = new GameView({model: game});
this.$el.append(gameView.render().el);
}, this)
return this;
}
});
var GameView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#gameTemplate').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var gameCollection = new GameCollection([
{
name: 'John Doe',
age: 30,
occupation: 'worker'
},
{
name: 'John Doe',
age: 30,
occupation: 'worker'
},
{
name: 'John Doe',
age: 30,
occupation: 'worker'
}
]);
var gamesView = new GamesView({collection: gameCollection});
$(document.body).append(gamesView.render().el);
This is one of the many things to love about Backbone. I don't know what you are using for your backend, but you state that you have a json file on your server, hopefully a json file full of the models that should be in your collection. And now here is the magic code (drumroll please..):
var GameCollection = Backbone.Collection.extend({
model: Game,
url: 'path/to/json/on/external/server',
});
var gameCollection = new GameCollection();
gameCollection.fetch();
Not much to it, right? Of course there are several options you can add or change to a fetch, so check out the docs here: http://backbonejs.org/#Collection-fetch. Backbone uses jQuery.ajax() be default, so check out the docs here to see all of the options: http://api.jquery.com/jQuery.ajax/
You shouldn't need the custom parse in your collection unless your models on the server don't match your backbone models.
Things to know:
fetch is asynchronous. It takes time to talk to the server, and the rest of your javascript will move on and complete. You will probably need to at least add a callback function to the success option, which will be called when fetch is finished, and it is good to add something to error as well, in case something goes wrong. You can add data as a query string so that your backend can use it using the data option, the data has to be an object. Here is an example:
gameCollection.fetch({
data: {collection_id: 25},
success: function(){
renderCollection(); // some callback to do stuff with the collection you made
},
error: function(){
alert("Oh noes! Something went wrong!")
}
});
fetch should receive data as JSON, so your url should either exclusive return JSON or be set up to detect an AJAX request and respond to it with JSON.
Firstly you need to fetch it from server as RustyToms said. And the other consideration is how to force the collection view to render itself again once data collected from server, as muistooshort commented.
If you manipulating fetch or sync you'll need to do it multiple times when there are more than one collection in app.
Doing such is native with Marionette, but in plain Backbone you can mimic the method of Marionette's CollectionView and do such:
//For the collection view
var GamesView = Backbone.View.extend({
initialize: function({
this.listenTo(this.collection, 'reset', this.render, this);
});
// Others
});
Then, when collection data fetched from server, the collection will trigger a reset event, the collection view noticed this event and render itself again.
For more than one collections, you can extract the code into a parent object in app and inherit from that.
var App.CollectionView = Backbone.View.extent({
initialize: //code as above
});
var GamesView = App.CollectionView.extend({
//Your code without initialize
});
I know this is a bit old at this point, but wanted to answer for anyone else stuck on this.
The code seems to come from the tutorial found here: http://codebeerstartups.com/2012/12/a-complete-guide-for-learning-backbone-js/
I too re-purposed the demo app found in that tutorial and had trouble rendering using external data.
The first thing is that the data itself needs to be converted to valid JSON or else you'll get a .parse() error.
SyntaxError: JSON.parse: expected property name or '}' at line 3 column 9 of the JSON data
or
error: SyntaxError: Unexpected token n
In your data source file, object properties need to be surrounded by quotes. It should look something like this:
[
{
"name": "John Doe",
"age": 30,
"occupation": "worker"
},
{
"name": "John Doe",
"age": 30,
"occupation": "worker"
},
{
"name": "John Doe",
"age": 30,
"occupation": "worker"
}
]
Secondly, once it's clear the external data is loading, we need to get it to render. I solved this (perhaps ungracefully) by moving the render() command into the success function of your gameCollection.fetch().
gameCollection.fetch({
success: function(collection, response, options) {
console.log('Success!! Yay!!');
$(document.body).append(gamesView.render().el);
},
error: function(collection, response, options) {
console.log('Oh, no!');
// Display some errors that might be useful
console.error('gameCollection.fetch error: ', options.errorThrown);
}
});
There are certainly better ways to accomplish this, but this method directly converts the code learned in the tutorial into something that works with external data.