Pass custom collection object from Controller to View with JQuery - c#

I have a custom object that derives from IEnumerable. This collection is pretty complex because it contains object which themselves contain collections of other objects. Simply put, it is a multiple dimension array.
I have a drop down list on my view that gets populated with a server call of items, and when a certain item is selected, it calls the server to get the associated collection object for that item.
I inherited this code and initially when the first drop down was selected a second drop down is enabled and the user selects a single item. The second drop down is populated with the items in the collection (the collection itself is parsed through to simply get the name and id number of the item).
Now, instead of a second drop down, I want to actually return the collection to the view and have my view loop through and display the contents of the collection and all that good stuff.
My question is how can I transfer this collection object from my controller to my view.
Here is the code in the controller which will grab the collection based on the value of the drop down.
public ActionResult GetWorkbooks(string term, int projectId = -1)
{
if (this.SelectedProject != projectId)
{
try
{
WorkBookDataManager dataManager = new WorkBookDataManager();
this.WorkbookColl = dataManager.GetWorkBooksById(null, projectId, null);
this.SelectedProject = projectId;
}
catch (Exception exc)
{
log.Error("Could not load projects", exc);
}
}
return this.View("_Workbook", this.WorkbookColl);
}
This code will return a partial view with the collection as a model.
But how can I use that with the existing JQuery code when the drop down has a value selected?
Here is the drop down code:
// Populate the first drop down
var loadProjects = function (request, response) {
$.ajax({
url: $("#projects").attr("data-vs-autocomplete"),
dataType: "json",
type: "POST",
data: { term: request.term }
})
.always(function (data, status) { getResponse(response, data, status); });
};
// If the first drop down has an item selected enable the second drop down
var projectSelected = function (event, ui) {
var projectId = $("#projects").data(VS_AUTOCOMPLETE_VALUE);
var enable = projectId ? false : true;
/*$("#workbooks").prop('disabled', enable);
$("#workbooks").val("");
$("#workbooks").data(VS_AUTOCOMPLETE_VALUE, "");
$("#workbooks").data(VS_AUTOCOMPLETE_TEXT, "");*/
$("#workbook").html("<p>No workbook selected</p>");
};
// Function to get the second drop down items
// This is the function I think needs to be modified to accept the collection object from the server
var loadWorkbooks = function (request, response) {
$.ajax({
url: $("#workbooks").attr("data-vs-autocomplete"),
dataType: "json",
type: "POST",
data:
{
term: request.term,
projectId: $("#projects").data(VS_AUTOCOMPLETE_VALUE)
}
})
.always(function (data, status) { getResponse(response, data, status); });
};
// Second drop down -> This needs to be removed
var workbookSelected = function (event, ui) {
$("#workbooks").blur(); // this prevents the workbook dropdown from focusing.
LoadWorkbook();
};
// These functions populated the drop downs with items
Autocomplete($("#projects"),
{ autoFocus: true,
minLength: 0,
source: loadProjects,
select: projectSelected
});
Autocomplete($("#workbooks"),
{ autoFocus: true,
minLength: 0,
source: loadWorkbooks,
select: workbookSelected
});
I want to make this simple so if there is a better way to do all of this and restructure the controller and/or jquery, I am all ears (eyes).
Let me know if more information is needed or if anything is unclear.
Thanks

"Best practice" here is single responsibility principle, i.e. separate actions for getting the data that should be displayed in dropdown and the same data that is rendered as partial view. Basically all you need is a method to retrieve the model, and one action that serializes the model and return in form of JSON, another - returns partial view. Controller:
private Workbook GetWorkbooksByProject(int projectId)
{
WorkBookDataManager dataManager = new WorkBookDataManager();
var workbookColl = dataManager.GetWorkBooksById(null, projectId, null);
return workbookColl;
}
public JsonResult GetWorkbooks(int projectId)
{
var model = GetWorkbooksByProject(projectId);
return Json(model, JsonRequestBehavior.AllowGet);
}
public ActionResult WorkbooksList(string term, int projectId = -1)
{
if (this.SelectedProject != projectId)
{
try
{
this.WorkbookColl = GetWorkbooksByProject(projectId);
this.SelectedProject = projectId;
}
catch (Exception exc)
{
log.Error("Could not load projects", exc);
}
}
return this.View("_Workbook", this.WorkbookColl);
}
From client side you must change the url to post data to GetWorkbooks action method and you are good to go.
Advantages of this approach is that populating the dropdown will not execute any other logic than retrieving workbooks list and at client side you can now easily leverage any binding framework (e.g. KnockoutJS) or plain javascript to render your model, even if your html markup will be changed in future from simple dropdown to more complex ui.

Related

ASP.NET Core MVC: Get values from Ajax, send to new View

This is similar to a previous issue that was resolved earlier. In this case, instead of rendering a table in the same view, I'd like to take the data retrieved and push to a new view:
Populate child area paths in a multiselect dropdown list
Select some of the items in the dropdown list
Take the selected items (as area path children) send back to controller
From controller use area paths as parameters to a method retrieving work items
Take workitem list and populate list in new view
Here is my view script
function GetSelectedAreaPathChildren3() {
alert("TEST1");
var selectedAreaPathItems = '';
$("#AreaPathMultiSelectDropdownList :selected").each(function () {
selectedAreaPathItems += this.value + ";";
});
var selectedIterationPathItems = '';
$("#IterationPathMultiSelectDropdownList :selected").each(function () {
selectedIterationPathItems += this.value + ";";
});
console.log(selectedAreaPathItems);
$.ajax({
url: '#Url.Action("WorkItemTable", "PbcAzureDevOps")',
type: "POST",
data: { selectedAreaPathItems, selectedIterationPathItems },
success: function () {
alert("BLAH!");
}
});
}
Here is my controller method
public async Task<IActionResult> WorkItemTable(string selectedAreaPathItems, string selectedIterationPathItems)
{
//Get the retrieved work items
List<WorkItemInfo> retrievedWorkItems =
await GetAllSelectedWorkItems(selectedAreaPathItems, selectedIterationPathItems);
return View(retrievedWorkItems);
}
Expected result:
retrievedWorkItems is populated and sent to view ("WorkItemTable") -- after sending to view, "WorkItemTable is shown on screen
Actual result:
retrievedWorkItems is populated and sent to view ("WorkItemTable") -- after sending to view, "WorkItemTable is NOT SHOWN (i.e. WorkItemTable does not pop up)
I recognize from my research that I can't get the view to show up from the script and have also tried adding the following to the view:
#using (Html.BeginForm("WorkItemTable", "PbcAzureDevOps", FormMethod.Post))
{
}
Can anyone help me to get the WorkItemTable to render... I can already see the data in the foreach part of the view, it just doesn't show.
So I figured a workaround... instead of pulling the selections from the dropdownlists using Ajax, I pulled the data from the dropdown via IFormCollection:
public async Task<IActionResult> WorkItemTable(IFormCollection collection)
{
List<string> selectedAreaPathItems = collection["apchildrenDropDown"].ToList();
List<string> selectedIterationPathItems = collection["ipchildrenDropDown"].ToList();
//Get the retrieved work items
List<WorkItemInfo> retrievedWorkItems =
await GetAllSelectedWorkItems(selectedAreaPathItems, selectedIterationPathItems);
return View(retrievedWorkItems);
}

Model saving and manipulation in the view for 2 model lists

I have a MVC Model that will have 2 lists as well as some other properties. List 1 is a check box list. List 2 is a list of start and end times. I have these set up and working but now I need to be able to put more data into list 2 which I have achieved with Jquery but adding in javascript. This does not add it to the model data. I then need to post both of these lists to a controller action so that I can loop them and post them into the database(not my issue). I need to fill the models to be sent. I am unsure how to get that in and am drawing a blank on filling the second model using jquery/js.
I could do this in single entries rather easily but I want to be able to submit more than one at a time.
I have tried creating a partial view with just the model list data in it which presents but then I have the same problem with filling the model. I have tried just doing it through MVC but I am not saving as I go I want the customer to be able to put in a few changes and then save. So it has to be saved on the DOM and I am using a table for that. I want that table to be model data.
model
public List<PersonelList _ViewModel> PersonelList { get; set; }
public List<OutTimesForList_ViewModel> OutTimesList { get; set; }
public DateTime? StartDateTime { get; set; }
public DateTime? EndDateTime { get; set; }
They are nullable (datetimes) because of a JS datetime picker I am using shows a time in the textbox already if I dont send it nullable.
In the view they would enter a start time and end time hit add then I want it to be added to the OutTimesList so that the post would give me that data filled in the model list. The personelList is the CB list for times that will be added, so if checked then add these times.
When a post is done both lists would be sent to the controller for me to traverse and submit data.
Try to use jQuery ajax post with formData. With collection of object it should look like this:
$( "#YourFormId" ).submit(function(event) {
var formData = new FormData($("#YourFormId"))
//todo need to get properties of PersonelList and OutTimesList via jquery
var indexPersonelList = 0;
for(var pair of PersonelList){
var item = pair[key];
formData.append("PersonelList[" + indexPersonelList + "].Id", item.Id);
indexPersonelList++;
}
var indexOutTimesList = 0;
for(var pair of OutTimesList){
var item = pair[key];
formData.append("OutTimesList[" + indexOutTimesList + "].Id", item.Id);
indexOutTimesList++;
}
$.ajax({
type: 'POST',
url: '/MyController/MyAction',
data: formData,
contentType: false,
processData: false,
success: function (data) {
//some success stuff
},
error: function (error) {
//some error stuff
}
});
});

Jquery .post method is sending a null value. How to pass actual value to controller?

I have a controller that applies to an edit view in asp.net MVC. I have an actionlink that sends the row Id to the controller which then brings back the correct row to see in the associated view.
I then have a partial view below that. That also requires a parameter in order to bring associated data from another table.
I have a Jquery .post call that runs after the page is loaded. I can alert out and show the exact value I want to send to the controller.
$(document).ready(function () {
var url = "/Home/MmsAndNotes";
var Uc = $("#Id").serialize();
alert(Uc);
$.post(url, {Id: Uc}, function (data) {
alert("what is Uc now? " + uc); //just for testing
});
})
I have also used it this way.
$(document).ready(function () {
var url = "/Home/MmsAndNotes";
var Uc = $("#Id").val();
alert(Uc);
$.post(url, Uc, function (data) {
});
})
the alerts come up and show the value I want. However, when the .post call runs, it sends a null value. Here is my controller.
public ActionResult MmsAndNotes(string Id)
{
//Declare LogisticsVM for individual policy info
LogisticsMMS_NotesVM model;
if(uc == null)
{
return Content("uc is empty.");
}
int val = Convert.ToInt32(uc);
using (Db db = new Db())
{
LogisticsMMS_NotesDTO dto = db.LogisticsMMS.Find(val);
//confirm policy exists
if (dto == null)
{
return Content("This policy cannot be found." + val);
}
model = new LogisticsMMS_NotesVM(dto);
}
return PartialView(model);
}
It always returns as uc is empty. I repeat, when the alerts come up. I get the correct value to send to the controller. But once it sends, something happens and it converts to null. HELPPPPP.. please .. I'm losing my mind over this one.
I don't know why, but changing my $.post() call to an $.ajax({}) call solved the issue. As you can see above, I had the $.post call. Using this instead,
$.ajax({
type: "POST",
url: "/Home/MmsAndNotes",
dataType: 'text',
data: { Id: Uc }
});
Solved it. I thought Jquery's shortened calls worked the same way. They certainly might, but doing it this way was the only way it worked for me.
P.S. Thanks Tyler (above) for your comments.
this solution should be work :
$(document).ready(function () {
$.ajax({
url: '/Home/MmsAndNotes',
type: 'GET',
dataType: "html",
data: { uc : $("#Id").val() },
success: function (result) {
code here
}
});
})
You need to verify if $("#Id").val() is not empty

Model not updating after jquery ajax post

I'm working on a application which should return multiple list based on selected values.
When a organisation is selected a few queries will run to populate a selectlist.
These queries only return data relevant for the selected organisation.
After that there is a dropdownlist to select which selectlist should be displayed. This will populate another dropdownlist with the data returned from the query.
On the initial load it is loaded correctly for the first organisation. However when another organisation is selected, an ajax post will call a method. this will run the queries succesfully and set the properties to the returned lists. however the problem is that the razor page still uses the data from the initial load.
this results in the fact that whatever organisation i will select, it will use the data from the queries for the first organisation on the razor page.
below the ajax call
$.ajax({
method: "POST",
url: "/Aanleveringen/Create?handler=Filter",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
dataType: "json",
data: { organisatieId: $('#ddlOrganisatie option:selected').val() },
success: function (msg) {
alert(msg);
},
error: function (req, status, error) {
alert("Error try again");
}
});
and the method:
[HttpPost]
public IActionResult OnPostFilter(int organisatieId)
{
Filter filter = new Filter();
Organisatie organisatie = _context.Organisatie.Distinct().Where(x => x.Id == organisatieId).First();
FilterWaardeGemeente = filter.GetFilterGemeente(organisatie);
FilterWaardeDienst = filter.GetFilterDienst(organisatie);
FilterWaardeClient = filter.GetFilterClient(organisatie);
return new JsonResult("Gelukt");
}
looking for any suggestion to have the razor page model update with the most recent c# pagemodel.
thanks in advance.
Changing your model isn't going to do anything, as the Razor page is not being re-rendered. Since you're making the request via AJAX, you need to return your select list items from that action, and then use JavaScript to update the select list options manually in the DOM.

Save a dynamic data set using AJAX in MVC

I have a razor view in which I generate the controls on the page generically by using a foreach loop on a list of data from a database. I am using DevExpress for my controls but I don't think that matters too much for solving this problem.
var docTypes = MyProject.GetDocumentTypes();
foreach(var docType in docTypes)
{
//Add controls (Example Control Below)
groupItem.Items.Add(i =>
{
i.Name = "checkDoc" + docType.Id;
i.NestedExtensionType = FormLayoutNestedExtensionItemType.CheckBox;
});
}
Usually when I write an AJAX call to save this data, I would write something like this. Note that this is being called when clicking a save button specifically:
function updateDocs() {
$.ajax({
url: '#Url.Action("SaveDocs", "Home")',
data: {
key: '#(Model.Id)',
docChecked: checkDoc1.GetValue(),
}, success: function(data){
},
error: function(data){
}
});
}
Then with the method:
public JsonResult SaveDocs(int key, bool docChecked)
{
//Save the data here
return Json(new { isSuccess = true },
JsonRequestBehavior.AllowGet
);
}
However, if I am generating the controls at runtime, I cannot know what exactly there is to pull for my parameters for the data section.
I was thinking that if I could somehow get this data into a Dictionary or something then I could pass it generically to a function with a parameter that accepts that Dictionary. If I can pass just some key data points to my Controller I can figure out how to save it from there. I would just need the value that the control holds and the control name itself passed in the simplest solution.
Thanks in advance for any assistance with this problem.
in your controller you can use this (this allows you to send any number of params of any type and parse them in the Action)
[HttpPost]
public JsonResult PostData(dynamic postData)
{
Dictionary<string,string> data = JsonConvert.DeserializeObject<Dictionary<string, string>>(((Newtonsoft.Json.Linq.JObject)postData)["data"].ToString());
...........
}
Or just send a dictionary
[HttpPost]
public JsonResult PostData(Dictionary<string,string> postData)
{
.......
}
The js call (for dynamic data type)
$http({
method: "POST",
url: Helper.ApiUrl() + '...',
data: { customer, applicant, accountNumber,... },
})

Categories