AJAX call is sending null data to controller action - javascript

I'm currently writing a website using ASP.NET Core, HTML, and Javascript. Currently, I have a page of the website that loads a chart of Equipment and stats on them. Below the chart is an area where the user can select specific equipment whose details they want to view. This info is then sent to the controller, which returns info back to the View about them.
However, while I know that the AJAX call does go to the controller when I look at it with breakpoints, and I know the value of my selected equipment is corrected, it always returns a "null" string value to the controller and I don't know why.
I've attempted a couple of different ways of writing it according to code I've seen on SO. My code was similar (structurally speaking) to the code on this page. I attempted the solution, which is the code I have now, but it didn't seem to change anything.
Here is my current javascript code:
function filterInterventions() {
var equipment = $(".equipmentCheckboxes:checkbox:checked").map(function () {
return this.value;
}).get();
$("#IntrvTable tbody tr").remove();
$.ajax({
type: "GET",
data: equipment,
cache: false,
//url: "GiveData?jsonString=",
url: '#Url.Action("GiveData")',
success: function (data) {
if (data) {
alert("success");
var parent = $("#parent");
parent.empty();
parent.append(data);
}
}
});
}
And here is my controller code:
public ActionResult GiveData(string jsonString){
//do stuff....
return View("RawData", myModel);
}
When I step through it in the browser and on my IDE, I know that the equipment in the actual javascript is correct. For example, if I selected equipment 'AA' and 'BB' to look at, I know I'm sending in ["AA/", "BB/"]. But I just cannot get the "jsonString" in the controller action to be non-null.
I thought it might be because I'm sending in an array, but the things I've found online about turning on traditional have also not helped.
I would love any help. Thanks.

I believe what you'd be looking for would be:
data: { 'jsonString': JSON.stringify(equipment) }
Normally though JSON would be used for an object rather than an array of strings, and the method signature would reflect the object definition of the data being passed as a POCO class. If you just want an array then something like this should work:
data: { 'equipment' : equipment }
with the controller action signature:
public ActionResult GiveData(string[] equipment)
Edit: JSON / POCO example
For a typical JSON example let's say you have a view model representing editing a new or existing order. This may have an Order ID, an order date, a customer ID, delivery address details, and a list of order lines. (product ID, quantity) On the client side you would construct a JS object view model for the data and then compose that to JSON to look something like:
{
"order": {
"orderId": 1234,
"orderDate": "2019-07-21",
"customerId": 216,
"deliveryAddress": {
"street": "216 Cuckoo Crescent"
"city": "Sydney"
}
"orderLines": {
{ "productId": 22, "quantity":1 },
{ "productId": 15, "quantity":3 }
}
}
}
where the Ajax call data component would look like:
data: { 'order': orderJson }
If the order data was just a Javascript object then you would use JSON.stringify(order) to pass the JSON representation.
In your server you would define these view models:
[Serializable]
public class OrderViewModel
{
public int? OrderId { get; set; }
public DateTime OrderDate { get; set; }
public int CustomerId { get; set; }
public AddressViewModel Address { get; set;}
public ICollection<OrderLineViewModel> OrderLines { get; set; } = new List<OrderLineViewModel>();
}
[Serializable]
public class AddressViewModel
{
public string Street { get; set; }
public string City { get; set; }
// ...
}
[Serializable]
public class OrderLineViewModel
{
public int ProductId { get; set; }
public int Quantity { get; set; }
}
In your method signature you would like:
public ActionResult CreateOrder(OrderViewModel order)
ASP.Net will accept the JSON object and your code can work with the POCO classes automatically. This is pulling from memory, and might require/involve a library like JSON.Net with some attribute goodness or extra steps so there may be some tweaking and config needed, especially to accommodate Pascal vs. Camel casing conventions between the JS and C# code. The onus though is on the developers to keep the POCO definitions and JS objects in-sync, and ensure the data types are compatible.

It is now quiet a default behavior - to map raw data to plain string.
Instead of mapping to string it is usually a better way - to map the JSON to the corresponding class type.
If raw data is the goal - here might be something you are looking for.

Related

How do I get a JavaScript array of an array of strings into a C# data structure?

I've been trying for the past hour and can't get it. At this point my controller looks like
public ActionResult GenerateMasterLink (string assetsJSON)
{
...
}
and I've confirmed that it is being passed a string like
[["Microsoft","Azure","roman_column.png"],["Microsoft","Azure","runphp.cmd"],["Microsoft","Azure","runphp.cmd"],["Microsoft","Azure","Picture1.png"],["Microsoft","Azure","vertical-align-scrnsht.png"],["Microsoft","Azure","vertical-align-scrnsht.png"]]
The only question I have is how I get the damned stuff out of it!
I've tried creating a class
public class ThreePartKey
{
public string organizationName { get; set; }
public string categoryName { get; set; }
public string fileName { get; set; }
}
and then done
ThreePartKey [] assetList = new JavaScriptSerializer().Deserialize<ThreePartKey []>(assetsJSON);
which gives me
Failed to load resource: the server responded with a status of 500
(Internal Server Error)
in my browser's console some of the time, and other times gives me
Additional information: Type
'AllyPortal.Controllers.SurfaceAssetsController+ThreePartKey' is not
supported for deserialization of an array.
as a Visual Studio error.
I've experimented with a million things and can't get this right. All I want is to have the JSON in some C# data structure where I can actually use it in my controller. Any suggestions?
You are trying to deserialize to incompatible model. You string input can be deserialized into string[][] variable but in order to allow deserializations into an ThreePartKey you need to name those values per property:
[[organizationName: "Microsoft",...]]
This will copy right value to your model
The problem is that your target datatype doesn't match the source datatype.
If you are converting an array of array of strings, you must deserialize into another array of array of strings, only them, you'll be able to convert it into whatever you want:
In short, replace
ThreePartKey [] assetList = new JavaScriptSerializer().Deserialize<ThreePartKey []>(assetsJSON);
for
ThreePartKey[] assetList = new JavaScriptSerializer().Deserialize<string[][]>(assetsJSON)
.Select(el => new ThreePartKey() {organizationName = el[0], categoryName = el[1], fileName = el[2]})
.ToArray();

AngularJS Umbraco Save Node Issue

I have a quick question. I've created a custom drop-down HTML control that dynamically grabs "nodes" from a database. I turned this into an Umbraco (v7) datatype that can be used as an Umbraco property in the admin section.
Unfortunately, when the user picks one of the items and saves, it does not properly store this info. I've determined that this is because when Umbraco tries to save to the DB, it's NodeId is empty. I need to populate this before we save the data. Unfortunately, I haven't found any easy way to do this.
Can anyone point me in the right direction? Populating the NodeId before I push the model to the CS Controller (which should update the SQL DB) should work... or is it possible to make an angularJS that pushes TWO parameters and let the C# handle this?
Anyone have any ideas?
Here's the HTML Editor:
<select ng-controller="Dmns.DDLAllNodesController" data-ng-model="selectedOption">
<option ng-repeat="gNode in galaxyNodes.data" value="{{gNode}}">
{{gNode.KioskNodeId}} -- {{gNode.MachineName}}
</option>
</select>
And here's the AngularJS, where I'd like to add the Umbraco's Node Id to the model before submitting to the C#
$scope.$on("formSubmitting", function () {
$scope.selectedOption({
Id: $routeParams.Id // This does not update properly
});
$scope.$apply();
getAllNodesResource.save($scope.selectedOption); // Sends old data
});
And here's the model I'm returning, in case this helps:
[Serializable]
[DataContract]
public class KioskNode
{
[DataMember]
public int Id { get; set; }
[DataMember]
public int NodeId { get; set; }
[DataMember]
public int KioskNodeId { get; set; }
[DataMember]
public string MachineName { get; set; }
}
So I found the reason why it wasn't populating correctly. I wasn't deserialing the object from JSON, which means I couldn't get the child properties.
This fixed my problem:
var deJson = angular.fromJson(node);
return $http.post("/umbraco/backoffice/api/DDLNodes/PostSave", {
Id: 0,
NodeId: id,
KioskNodeId: deJson.KioskNodeId,
MachineName: deJson.MachineName
});
This let me pass the variables (updated) I need back to Backoffice to save it to the DB.

dynamically creating a full one to many relationship on a single page

I'm new to MVC, but have done a bunch of reading the past few months, and I've ran into some issues when trying to build a one to many relationship dynamically on the page. I've set up the logic and can accomplish this through multiple page loads but I want the form to be more dynamic, so I'm not sure what the correct way to do this is.
For testing purposes I scaled down my Model just to see if I can get the interaction I want before I apply it to my project and what I have is this:
Generated models from my Db.
public partial class TestIncident
{
public TestIncident()
{
this.TestGeoKits = new HashSet<TestGeoKit>();
}
public int RecordID { get; set; }
public string OwnerFirstName { get; set; }
public string OwnerMiddleName { get; set; }
public string OwnerLastName { get; set; }
public virtual ICollection<TestGeoKit> TestGeoKits { get; set; }
}
RecordID being my PKey for the class above
public partial class TestGeoKit
{
public int RecordID { get; set; }
public int KitID { get; set; }
public bool SubmittedForTestingFlag { get; set; }
public string SubmissionLocation{ get; set; }
public Nullable<System.DateTime> SubmissionDate { get; set; }
public bool Processed { get; set; }
public Nullable<System.DateTime> ReturnDate { get; set; }
public string Notes { get; set; }
public virtual TestIncident TestIncident { get; set; }
}
RecordID being my FKey relation and KitID being my PKey for this table.
Now, I need to set up a Create View that contains both the Form associated with the TestIncident AND has the option to click an ADD button to populate Partials containing the Form associated with my TestGeoKits on the same page, instead of after a postback and redirect (How I currently have it). I've tried piecing information from multiple posts together to accomplish this, but one way or another I run into a bunch of problems and even more questions..
Logically I would think, being MVC uses Entity Framework, that on my Create TestIncident page I can have an ADD button that adds a TestGeoKit form to the page each time its clicked, in the form of a partial view. Then, when you click submit the entire Object is passed back to the Controller, binds all the values for the incident to the database, gets the next Identity Value (set up in sql) for RecordID, and uses that information for the ICollection of TestGeoKits in TestIncident to build the foreign key relationship and save all the data.
How do I go about building a dynamic page like this? I've read MANY articles, played with Ajax commands and jQuery form controls, played with LazyLoading / EagerLoading, and I think I'm over complicating it for myself at this point.
I have a, not so dynamic, version currently running that when you submit your TestIncident form, the Method called in the Controller checks for a GeoKitCollectedFlag after its saved and if its true redirects you to a separate page to fill out the GeoKit Form. But, again my ideal form is to combine these two actions on one page.
Any help would be greatly appreciated!
Really the key thing is in the naming of your fields. MVC is actually pretty competent at constructing complex object graphs based on posted data from a form, as long as you simply follow its conventions. For enumerable properties, that's making sure your field names follow the form ListProperty[N].Property. So, for your TestGeoKit instances you should have input names like TestGeoKits[0].RecordID.
To ensure this happens with Razor generated inputs, you need to iterate over existing instances using for rather than foreach:
#for (var i = 0; i < Model.TestGeoKits.Count(); i++)
{
#Html.HiddenFor(m => m.TestGeoKits[i].RecordID)
<!-- etc -->
}
Adding new records dynamically obviously requires a JavaScript solution. There's many different ways you could handle this. You could construct the HTML manually via JavaScript. You could submit an AJAX request to some action that would return HTML for all the fields. You could use JavaScript to copy the HTML for an existing record on the page and replace the id and name attributes with the proper index.
Personally, I would recommend going with a framework geared towards handling data binding and templating. Knockout is a popular choice, but your could use things like Backbone, Angular, etc. The idea, simply, is that you want something where you can merely add a data object to an array in JavaScript, and have it generate the appropriate HTML for you with the right index for the names and such. Trying to keep up with this manually is kind of a nightmare.

Is there any way I can do type and field name checking with javascript json data received on my client

I have the following class on my server:
public class JsonDialog
{
public IEnumerable<string> Errors { set; get; }
public string Modified { set; get; }
public string ModifiedBy { get; set; }
public string PartitionKey { set; get; }
public string PartitionKeyNew { set; get; }
public string RowKey { get; set; }
public string RowKeyNew { get; set; }
public bool Success { set; get; }
}
In my MVC action method I return a JsonResult:
return Json(new JsonDialog
{
Success = false,
Errors = errors
});
In my client code I have the following:
$.ajax({
url: href,
dataType: 'json',
type: 'POST',
data: $form.serializeArray()
})
.done(onDone)
.fail(onFail);
var onDone = function (json, textStatus, XMLHttpRequest) {
json = json || {};
if (json.Success) {
Because I am using a class on the server the compiler checks to ensure I don't make any spelling mistakes and for example it will not allow me to enter "success = false". However on the client there's no checking and I could code json.sUccEss and it would still be okay with that but it would not give me the desired result.
Is there any way in Javascript that I can have some error checking like I have on the server? Anything like getting data into a class and having the IDE check that class fields are correct?
I solve this (partially) by creating a matching object in JS with, what I refer to as a 'factory' method (but that's a misnomer if you're using the term 'factory' as is referred to here. It's really more analogous to 'casting', but since JS doesn't have strong typing, that's not exactly right either.
Obviously, this code pattern is also pretty verbose if all you're doing is trying to get intellisense working, but if you have methods on your JS-side object... we'll fictionally say: JsonResult.prototype.markErrorsReviewed...then this pattern where you get an instance of a given 'class' is helpful.
For the sake of brevity, I'm going to just use your C# class on which to base my JS:
var JsonResult = function () {
this.Errors = [];
this.Modified = null;
this.ModifiedBy = null;
//...rest of your properties, defaulted as necessary
}
JsonResult.factory = function (o) {
var j = new JsonResult();
$.extend(j, o);
//do other processing here if needed (for example, ASP.NET serializes DateTime retardedly)
return j;
}
AJAX:
$.ajax({
success: function(json, status, xhr) {
var myobj = JsonResult.factory(json);
//VS2010 will now give you intellisense w/ proper property names on myobj
//Note: it's still buggy and sometimes won't detect things properly, but it usually works
}
});
May be there could be tools but if you can achieve that following ways:
In the controller you render the partially view instead of json result and pass the class object on that then on that view you have to write the objcetName.PropertyName and it would you check it.
Maybe there are good Text Editors that would help you, but there is no such thing for Javascript.
Editors like TextSublime can fake the code introspection and give an in file search to give help typing the same names.
IDE like Eclipse or Netbeans do a better job but are still really far away from what you want.
Javascript is not a language strongly typed like Java or C# (if we can say that they are...) so you won't find all those candy features you have with VS and C#.
This is what NetBeans does and this is not as advanced as you wish.

Passing Objects from Views/Javascript to MVC Action

I am using MVC.
I have a view model which contains a Business object.
I need to post this Business Object back to a controller action using AJAX.
For now, I am using Url.Action function to create my request Url for AJAX and pass the Business Object id as a request parameter to the action. I then have to retrieve the object from the database to use it.
Is there a way (using Json) to pass this object as an Object? The object is shown below
public class BusinessObject : BaseBusinessObject
{
public virtual string Id { get; set; }
public virtual IDictionary Data { get; set; }
public virtual IDictionary Collections { get; set; }
}
The controller action should ideally have a deifinition like ...
public ActionResult DOThis(BusinessObject bo) {}
What you are looking for is FORM Binding, there are lot of resources available, This link gives you some insight.
You should use the JsonResult for going from the server to the client. http://www.ajaxprojects.com/ajax/tutorialdetails.php?itemid=399#start
The DefaultModelBinder will take care of the binding to the server. There is also a section in this page talking about calling from JQuery to the server and getting back the JsonResult.
Hope this helps!

Categories