I have a service call that gets a filtered set of historical records from our database.
I am having some trouble getting my filter to make it to my main mvc call.
[AcceptVerbs("POST")]
public History History( HistoryFilters filters){......}
config.Routes.MapHttpRoute(
name: "DefaultActionRoute",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
amplify.request.define('post', 'ajax', {
url: '/api/{controller}/{action}',
dataType: 'json',
type: 'POST',
cache: cache
});
//History Filter js
{
DateRange: { Start: ..., End: ...},
BetStatus: "unresolved",
TransactionTypes: "bets"
}
Every time I make my call filters is filled with null values. The JS filter structure matches that of my HistoryFilters class in C#. If I JSON.stringify my filters and change my Api call to string then i get that but it would be nice if it could convert it for me.
Can anyone tell me what i'm missing?
Edit
var amp = function(id, data) {
return $.Deferred(function(dfd) {
amplify.request({
resourceId: id,
data: data,
success: dfd.resolve,
error: dfd.reject
});
}).promise();
};
History: function (filters) {
return amp('post', { controller: 'user', action: 'history', '':filters});
}
public class HistoryFilters
{
public DateFilter DateRange { get; set; }
public string BetStatus { get; set; }
public string TransactionTypes { get; set; }
}
public class DateFilter
{
public string Start { get; set; }
public string End { get; set; }
}
It is my understanding that AmplifyJS has an issue with setting contentType and therefore can not make this type of call. I removed the amplifyJS call for now and am using just jquery.
Related
So, I'm using the feth api to do posts, and it's generally working out really well. But I am having one issue, say I have the following two models:
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public List<Phone> Phones { get; set; }
}
public class Phone
{
public string Number { get; set; }
public string Model { get; set; }
}
Now, I want to create a formData object from the html form, which adds the strings and ints just fine. but when I do:
var data = new formData(document.querySelector('form'));
let arr = [{
Number: "12345678"
},
{
Number: "87654321"
}
];
data.append('Phones', JSON.stringify(arr));
//OR
data.append('Phones', JSON.stringify(arr[0]));
data.append('Phones', JSON.stringify(arr[1]));
//OR
data.append('Phones[]', JSON.stringify(arr[0]));
data.append('Phones[]', JSON.stringify(arr[1]));
//OR
data.append('Phones[0]', JSON.stringify(arr[0]));
data.append('Phones[1]', JSON.stringify(arr[1]));
and then post to something like this:
[HttpPost]
public ActionResult SaveContact([FromForm]Contact contact)
{
...
}
The object is bound as its supposed to, with the correct values for Id, Name, But Phones contains nothing. Does anyone have some input here, its driving me nuts. Thanks!
Try to change your code to the following form:
let arr = ["123456","7895555"];
formData.append('Phones[0].Number', arr[0]);
formData.append('Phones[1].Number', arr[1]);
Result:
Array type property with complex objects
const fd = new FormData();
const phones = [
{ number: '123', model: 'model x' },
{ number: '456', model: 'model y' },
];
for (var i = 0; i < phones.length; i++) {
fd.append(`phones[][${i}][number]`, phones[i].number);
fd.append(`phones[][${i}][model]`, phones[i].model);
}
Starting from the great solution posted by Yiyi You (very thanks for your post, it helped me a lot) I would recommend also the following solution for my problem that was slightly different.
Javascript code:
var formData = new FormData();
let arr = ["123456","7895555"];
formData.append('Phones[0]', arr[0]);
formData.append('Phones[1]', arr[1]);
$.ajax({
type: "POST",
url: "/Contacts/SaveContacts",
data: formData,
processData: false,
contentType: false,
success: function(result) {
console.log(result);
},
error: function(xhr, resp, text) {
console.log(xhr, resp, text);
}
});
C# code:
[Route("[controller]")]
public class ContactsController : Controller
{
public ContactsController()
{
}
public class PhonesList
{
public Dictionary<int, string> Phones{ get; set; }
}
public async Task<JsonResult> SaveContacts([FromForm] PhonesList phonesList)
{
}
}
If you like, you can cycle with the forEach method in an array
arr.forEach((obj,index) => formData.append(`listName[${index}]`,obj.Number))
for ES5 >=
I have two classes: Person and account. Im working in asp net.
This is my controller:
public List<Person> GetPeople()
{
using (var db = new PeopleContext())
{
return db.People.ToList();
}
}
Person has the following attributes:
public class Person
{
public int PersonId{ get; set; }
public string Name{ get; set; }
public List<Accounts> Accounts{ get; set; }
}
Account class contains the following attributes:
public class Account
{
public int AccoundId{ get; set; }
public string AccountName{ get; set; }
public Person Person{ get; set; }
}
This is in my view
function getQuestions() {
$.ajax({
url: '/api/Person',
type: 'GET',
//dataType: 'json',
traditional: true,
success: function (data) {
$.each(data, function (i, p) {
string += '<h1>Name: '+p.Name+'</h1>';
$.each(p.Accounts, function (j, a) {
string += '<p>'+a.AccountName+'</p>';
});
});
},
});
How do i loop through account list in javascript and display it in my view? I have been trying to do this, but the list returns null. When i try to console log data it displays Accounts as null. How do i fix this?
I believe you would need to show your /api/Faq webservice response.
I would also suggest opening F12 developer tools and putting the word debugger in after you get the name:
string += '<h1>Name: '+p.Name+'</h1>';
debugger;
Then you can look at the p.Accounts object and see what it is.
If you Accounts object is empty you may need to add to your Person object's constructor:
public Person()
{
Accounts= new HashSet<Account>();
}
I've the following object which I am passing into an MVC Controller:
this.JsonData = {
"__RequestVerificationToken": $('input[name=__RequestVerificationToken]').val(),
"searchMode": {
"mode": Number(mode.val()),
"pageSize": Number(pagesize.val()) || 5, "pageNumber": Number(pagenumber.val()) || 1,
"sortField": sortfield.val() || "Ref",
"sortDirection": sortdirection.val() || "desc"
},
"searchData": {
"Compare": Number(StdComparison.val()),
"SearchTextFrom": searchText.val(),
"SearchTextTo": searchTextTo.val()
}
This works ok, but I recent requirement has arisen whereby I am looking to encode this object for use with the javascript function Window.location
Suggestions I have used:
how-to-pass-complex-json-object-in-url-using-javascript
window.location + "?SearchCriteria=" + JSON.Stringify(this.JsonData);
Creates the following request:
Controller/Action?SearchCriteria={
"__RequestVerificationToken": "tokenvalue",
"searchMode": {
"mode": 2,
"pageSize": 5,
"pageNumber": 1,
"sortField": "Ref",
"sortDirection": "desc"
},
"searchData": {
"Compare": 1,
"SearchTextFrom": "From A",
"SearchTextTo": "To Z"
}
}
Whereas
window.location + "?SearchCriteria=" + this.JsonData;
Produced the following:
Controller/Action?SearchCriteria=[object%20Object]
With a page not found error on both of the above.
UPDATE:
I have moved forward in the quest for an answer.
OK as per request from helpers, I have included more source code.
I have Three Classes.
public class MainSearch
{
public MainSearch()
{
SearchData searchData = new SearchData();
SearchMode searchMode = new SearchMode();
}
public SearchData searchData { get; set; }
public SearchMode searchMode { get; set; }
public int? page { get; set; }
public object ToPagedListParameters(int pagenumber)
{
searchMode.pageNumber = pagenumber;
return page;
}
public IList<string> ValidationErrorMessages { get; set; }
}
public class SearchData
{
// Fields used for the ticket number search
public int? ticketNumberCompare { get; set; }
public string ticketSearchTextFrom { get; set; }
public string ticketSearchTextTo { get; set; }
}
public class SearchMode
{
public int? mode { get; set; }
public int? pageNumber { get; set; }
public int? pageSize { get; set; }
public string sortDirection { get; set; }
public string sortField { get; set; }
public string userURN { get; set; }
public string __RequestVerificationToken { get; set; }
}
These classes hold the criteria used for searching (SearchData has been truncated)
The following is my controller code:
[HttpGet]
public ActionResult DownloadFileCSV(MainSearch search)
{
string fileName = Server.MapPath("~/Content/Pdf/") + "somefile.pdf";
byte[] fileContents = System.IO.File.ReadAllBytes(fileName);
return File(fileContents, "application/pdf", "result.pdf");
}
And finally, the Ajax call that is made from the cshtml file.
$("#DownloadAttachmentCSV").click(function () {
$.ajax(
{
url: '#Url.Action("DownloadFileCSV", "Home")',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
data: JsonData,
type: "GET",
success: function () {
window.location = '#Url.Action("DownloadFileCSV", "Home")' + '?' + JsonData;
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
});
Oddly enough, the code above does actually work and the file is downloaded, but here is the problem. The JSON data is not populating the MainSearch variable.
So far the only way I can get the JSON data to populate the c# classes is to change the method to a POST.
Must this be the case?
You cannot pass complex javascript objects to a GET method like that. A GET has not body and to bind to a model, your query string name/value pairs must match the properties of the object your binding to. For example to bind to the mode property of the SearchMode property of MainSearch, you query string would need to include
....&SearchMode.mode=2....
Change the code your using to generate the javascript object to
var data = {
'searchMode.mode': mode.val(),
'searchMode.pageSize': pagesize.val() || 5,
'searchMode.pageNumber': pagenumber.val() || 1,
'searchMode.sortField': sortfield.val() || "Ref",
'searchMode.sortDirection': sortdirection.val() || "desc",
'searchData.SearchTextFrom': StdComparison.val(),
'searchData.Compare': searchText.val(),
'searchData.SearchTextTo': searchTextTo.val(),
}
and the ajax code to
$.ajax(
{
url: '#Url.Action("DownloadFileCSV", "Home")' + '?' + $.param(data),
type: "GET",
success: function () {
Note also the following
There is no point using Number(..) to convert the values to a
number - its all sent across the wire as text
A GET has no body so setting the ajax contentType option is
pointless
Your method does not return json so datatype: 'json' does not make
sense.
Passing the antiforgery token to a GET method is not necessary
Having said that, its unclear what your wanting to do here. The code in your MainSearch search() method never uses any values of the model. And it returns a FileResult which cannot work with an ajax call (but it can work with window.location, it which case the code in the success callback would need to be
var baseUrl = '#Url.Action("DownloadFileCSV", "Home")';
var queryString = $.param(data);
window.location = baseUrl + '?' + queryString;
but then its unclear why you making 2 calls to the method - the ajax call, followed by the redirect (the ajax call does nothing at all).
Anything you use as a query parameter should be properly URI encoded, especially with something like JSON:
window.location + "?SearchCriteria=" + encodeURIComponent(JSON.stringify(this.JsonData));
I'm not sure that the model binder is designed to convert JSON in a GET request into a class instance, but the attempts you've made above are definitely not the way to go.
Hi I want to pass an object to my view and work with it in JavaScript. I've created this model
namespace WebApplication1.Models
{
public class OpenlayerLayer
{
public OpenlayerLayer(string Layers,string name,string url,string style,Boolean isqueryable,string projection,string maxEx)
{
LAYERS = Layers;
Name = name;
URL = url;
STYLES = style;
queryable = isqueryable;
this.projection = projection;
maxExtent = maxEx;
}
public string LAYERS { get; set; }
public string Name { get; set; }
public string URL { get; set; }
public string STYLES { get; set; }
public Boolean queryable { get; set; }
public string projection { get; set; }
public string maxExtent { get; set; }
}
}
and I used this method in controller
public string getLayerlist()
{
OpenlayerLayer ol = new OpenlayerLayer("Test: GParcel", "Parcels", "http://127.0.0.1:8080/geoserver/test/wms", "", true, "EPSG:900913", "5725524.327803587, 3835467.5859751734, 5729564.058979791, 3841404.792330884");
var json = JsonConvert.SerializeObject(ol);
return json;
}
well, I used this in my view
$(selector).getJSON('getLayerlist', data, loadopenlayers(data, status))
this is 'loadopenlayers' method
function loadopenlayers(result,status) {
alert("Seems works fine");
var layer = JSON && JSON.parse(result) || $.parseJSON(result);
var wms2 = new OpenLayers.Layer.WMS(
layer.name, layer.URL,
{
LAYERS: layer.LAYERS,
STYLES: layer.STYLES,
format: format,
},
{
buffer: 0,
displayOutsideMaxExtent: true,
isBaseLayer: true,
projection: new OpenLayers.Projection(layer.projection),
minResolution: null,
maxResolution: 48,
numZoomLevels: 32,
maxScale: null,
minScale: 1271428,
maxExtent: new OpenLayers.Bounds(layer.maxExtent),
minExtent: null,
displayProjection: new OpenLayers.Projection("EPSG:4326"),
}
);
}
so It must work and then call loadopenlayers in JavaScript code,right?
Now I have a problem how do I work with result of getLayerlist in loadopenlayers
Is my method right way to communicate between models and JavaScript in view?
In fact I have a large number of JavaScript codes which must be customized using model parameters and I want a consistence and standard method to do it
Thanks very much
Better use jquery ajax call e.g:
$.ajax({
type: "POST",
url: '/ControllerName/getLayerlist',
// data: data,send data to controller from here
dataType: 'json',
//processData: false,
success: function (msg) {
//your json data will be available in msg
//here you can also call loadopenplayers() method
},error: function (error) {
console.log(error.responseText);
}
});
Whereas your controller method will look like
[HttpPost]
public JsonResult getLayerlist(string data)
{
OpenlayerLayer ol = new OpenlayerLayer("Test: GParcel", "Parcels", "http://127.0.0.1:8080/geoserver/test/wms", "", true, "EPSG:900913", "5725524.327803587, 3835467.5859751734, 5729564.058979791, 3841404.792330884");
return Json(ol , JsonRequestBehavior.AllowGet);//automatically converts to Json
}
function loadopenlayers(result){
var newResult = JSON.stringify(result);
//Now newResult is a json
}
You can call through ajax
$.ajax({
url: '/ControllerName/getLayerlist',
type: "POST",
cache:false,
success: function (data) {
//You can get your result data here
console.log("Data :"+JSON.stringify(data));
loadopenlayers(data);
},error: function (error) {
console.log(error.responseText);
}
});
function loadopenlayers(data){
var newResult = JSON.stringify(data);//This is your JSON result
}
Here is a custom list (that I created for paging of my results):
public class PagedList<T> : List<T>
{
public int PageIndex { get; set; }
public bool HasNextPage { get; set; }
// ...
}
This list I want to pass via JSON to the View:
[HttpPost]
public ActionResult StoreProducts(StoreDetailsViewModel model)
{
PagedList<Product> products = _productRepository.GetAll();
return Json(products);
}
In the view I want to access to tho custom properties of my extended list but it's not working:
function getStoreProducts() {
var form = $('#form');
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
success: function(result) {
viewModel.products = result; // <-- success
viewModel.hasNextPage = result.HasNextPage; // <-- fail, property is undefined
}
});
}
Can somebody tell me how to solve my problem?
PagedList should be it's own class that has a property on it that is List, that's the easier route. Otherwise you'll need to write a custom converter for your PagedList class to not serialize as an array, but an object.
public class PagedList<T>
{
public int PageIndex { get; set; }
public bool HasNextPage { get; set; }
public List<T> Items { get; set; }
// ...
}