Issue with Updating Model in Razor View - c#

first post. Trying so solve an issue I am seeing here between my Razor view and model. I have a popup window that is being fed a partial view and model.
public IActionResult ClickedCovid19Question(int id)
{
var existingQ = db.CustomerInfoItems.Find(id);
var suffix = existingQ.Suffix;
if (suffix.Length == 2)
suffix = suffix.Insert(0, "0");
var salesman = SalesmanHelper.GetSalesmanNum();
var par = db.Pars.Where(p => p.AccountNo == existingQ.CustNum).Where(p => p.Suffix == suffix)
.Where(p => p.SalesmanNumber == salesman).FirstOrDefault();
var clickedCovid19Model = new ClickedCovidQuestionModel
{ //insert model data here }
clickedCovid19Model.Machines = db.MachinePopulationItems
.Where(m => m.CustNum == existingQ.CustNum)
.ToList();
return View("~/Views/Covid19/_ClickedCovid19Question.cshtml", clickedCovid19Model);
}
This works great on the first page render. I see the data fill my UI elements that are called from the #model on the razor page. When my user updates a field here and submits, I use this function
function SaveClickedCovid19Question(idJS) {
C19ParChanged('#Model.Par');
var jsonJS = SerializeForm("#c19QuestionForm");
$.ajax({
url: '/Covid19/SaveClickedCovid19Question',
type: 'GET',
data:
{
id: idJS,
json: jsonJS
},
success: function (data) {
Alert(data);
RefreshLV("CovidQuestions");
HideWindow("#Covid19Question");
},
error: function (data, error, e2) {
debugger;
}
});
}
However, after the popup window is closed, and another popup is opened, we call that same ClickedCovid19Question IActionResult to populate our form again. It populates the UI fine with the new model it generated, debugging shows it creates a new model with all the correct data.
The issue arrises when a user submits this form again, the model on the Razor view seems to think it is still the model of the very first submission. An example of this is the first line of the javascript function. When the razor view was created, it had the correct #Model.Par data, as I could see when I created a few elements to display it. However, when trying to capture that data using #Model.Par, it captures the original Model.Par data.
Long story short, subsequent popups don't overwrite the Model data from the very first one. I am stumped, because this system works in so many other areas of our codebase.
I can fix this buy creating hidden elements that will store the data I need to send in any requests, but I feel like there has to be a better answer than that.
Edit: Below is an example I was using to test. The view part below will always display the correct par data in the id=parID input box. However, on the second popup and everytime after, if I was to run the simple javascript function below to find the data held by model, it will ALWAYS update to the data from the first model the page ever rendered, which seems inconsistent with other areas of my code that do work normally.
function updateParID() {
$("#c19QuestionForm").find("#parID").val('#Model.Par');
}
<input id="parID" type="text" disabled="disabled" value="#Model.Par"/>
<partial name="Forms/_FormDropDown" model=#(new FormDropDownModel { Name = "Par", Values = Lists.ParStates(), Value = Model.CallsPerYear}) />
<button type="submit" onclick="RefreshWindow('#Model.ID')">Refresh</button>
<button type="submit" onclick="updateParID()">Update Par ID</button>

That #Model.Par (or whatever prop you have in #Model) renders before anything shows up in browser.
Razor page will render your view and then pass it to browser.
so if you want to fetch data using ajax you should manually put data received from ajax into you html controls.
Have a nice coding day :)

So, I actually ended up solving this. My issue was, trying to bind the data from the #Model.Par or any model data INSIDE a javascript function will ALWAYS bind using the initial model. Every subsequent call to this function will ALWAYS only use that very first model.
My solution that I overlooked was to actually send the data to the function from the model as a parameter first, and not try to bind it inside the javascript function.
Example: My edit has these lines
function updateParID() {
$("#c19QuestionForm").find("#parID").val('#Model.Par');
}
<button type="submit" onclick="updateParID()">Update Par ID</button>
When I change the order of how I capture that model to this:
function updateParID(parID) {
$("#c19QuestionForm").find("#parID").val(parID);
}
<button type="submit" onclick="updateParID('#Model.Par')">Update Par ID</button>
This now correctly captures the model data that is present. I am sure there is some reason javascript works this way, but it is unknown to me, if anyone can shed light on it. Forgive me if it is a simple answer, I am new to web programming. Thank you all for helping out!

Related

ASP.Net Core MVC - FullCalendar - Events are not displayed

Using FullCalendar 5.11.3 in my ASP.Net Core MVC application I have a problem that events aren't displayed.
On debugging one can see that on calendar initialization the controllers action is correctly called and also the data that is returned is correct and follows the requirements (https://fullcalendar.io/docs/event-source-object)
JSON that is returned when accessing action directly
[{"id":1,"title":"Beata Kowal","start":"13-09-2022","allDay":true}]
Calendar is placed in a partial view (as in the code below) and used in home view (in the future I plan to use it in several views with different data sources).
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
events: {
url: '/Home/GetCalendarData',
success: function() {
alert("ok");
},
failure: function() {
alert("error");
}
}
});
calendar.render();
});
</script>
<div class="row">
<div id='calendar' />
</div>
In the HomeController there is an action responsible for getting data to be used for displaying events
// GET: Home/GetCalendarData
public JsonResult GetCalendarData()
{
List<Appointment> appointments = _context.Appointments
.Include(app => app.Employee)
.Include(app => app.Customer)
.ToList();
List<HomeIndexGetViewModel> appointmentsIndexViewModel = _mapper.Map<List<Appointment>, List<HomeIndexGetViewModel>>(appointments);
return new JsonResult(appointmentsIndexViewModel);
}
Calendar is rendering and the alert on success is also displayed. What is more in the developer mode there are no errors or warnings. Yet, events still don't display.
My initial guess:
It takes too long to fetch data from the database and before action returns its result calendar is rendered and events can't not longer be added. If so, then how can I change it to work in a desired way? Introduce JS callback?
The problem was with parsing the date. It was not capable to process the provided date format which was "dd-MM-yyyy". After changing to "yyyy-MM-dd HH:mm:ss" everything works fine and events are displayed correctly.

Loading a partial view dynamically and loading index from controller

I am at a loss on how to do this. I've tried every search I can think of. Any help/direction would be greatly appreciated.
The customer wanted to be able to load a partial view dynamically from a wysiwyg editor. So, I made a method in the controller that searches for a special tag that gets the name of the partial view. Got that to work fine. Now the customer wants to load and post data to/from the partial view.
I was able to get it loaded by doing this - finalhtml += PartialView("~/Views/Global/Partials/" + commandContents + ".cshtml").RenderPartialViewToString();
If I name the controller the exact name of the partial view plus controller ie: SamplePartialController, I can get the view post back to the controller, BUT I can't figure out how to load an initial index method on the partial no matter what I try.
It would be nice if I could define the initial index method and controller name programmatically, just not seeing a way.
I am fairly new to mvc, been doing asp.net for over a decade, and this may be very obvious, but I'm just not finding a way. Thanks!
Not sure I understand clearly the issue. I also had a lot of troubles working with partial views (to post data, to load them back if something wrong happened in the post action...).
It seems like you want to fill your partial view if some parameters are here when loading the index.
In the Controller.Index (or where you get your parameters) you can assign some ViewModel values, or use the ViewBag/ViewData.
In the index view, you can call where you need the RenderPartialAsync() based on your parameters setted previously
this.ViewData["error"] = Model.Error;
this.ViewData["partialGuid"] = Guid.NewGuid();
this.ViewData["foo"] = bar;
await Html.RenderPartialAsync("_PartialViewName", Model.PartialViewModel, this.ViewData);
And use your partial view the same way you do from an ajax call.
If you need to post data from your partial view, I struggled a lot with some things:
A FormContext is required in the partial view
#if (this.ViewContext.FormContext == null)
{
this.ViewContext.FormContext = new FormContext();
}
If you allow to push multiple times your partial view and post them, making a custom collectionId helps a lot when in the controller post action
#{
var collectionId = $"PartialItems[{this.ViewData["partialGuid"]}].{{0}}";
}
// then later...
<input class="form-control"
data-val="true"
data-val-required="#(string.Format(ApplicationResources.Error_Required, ApplicationResources.FieldFirstName))"
id="#(string.Format(collectionId, "FirstName"))"
name="#(string.Format(collectionId, "FirstName"))"
type="text"
value="#(string.IsNullOrWhiteSpace(Model.FirstName) ? string.Empty : Model.FirstName)" />
<span class="field-validation-valid text-danger"
data-valmsg-for="#(string.Format(collectionId, "FirstName"))"
data-valmsg-replace="true"></span>
// And in your Index ViewModel, something like that to get the collection in the controller post action
public IDictionary<string, PartialViewModel> PartialItems { get; set; }
If you call the new partial view with Ajax, after the first render of the view, reset the jquery validator if needed
$.ajax({
// ...
success: function (res) {
// do your stuff
$('#parentId').prepend(res);
// reinit the validation
$("form").each(function () { $.data($(this)[0], 'validator', false); });
$.validator.unobtrusive.parse("form");
}
});
It is almost everything I can think of that bugged me when dealing the first time with all this partial view things. Hope something here can help you.

Data in view different thank backend

To put simply, I am scanning a drivers license with a magnetic strip card reader, using javascript RegEx to get the data I need, sending that data to the server as JSON via an ajax call, and then sending back a partial view with an Ajax.BeginForm with the data filled out.
In fact, all of this is working great. The problem is that I'm trying to alter the data on the server in C# before sending it to the view, but no matter what I do the original non-formatted data is showing up on the page still. I even stepped through the entire process, and looking at the Model data for the view shows that it received the correct data. Yet when the view is displayed, the non-formatted data (which does not actually exist in the Model object when I browse it) is what shows up in the text boxes.
function LoadVisitorCreatePartial(CardData) {
$.post(webroot + "Visitor/GetVisitorLogCreateVisitorForm", CardData, function (data) {
$('#visitor-info').append(data);
});
}
Above is the relevant javascript I am using for the call. I am using Carl Raymond's jquery card swipe to pull the data and I implemented some of my own RegEx to get the data I need for "CardData", which is JSON.
[HttpPost]
public ActionResult GetVisitorLogCreateVisitorForm(DriversLicenseForm CardData)
{
var form = visitorService.GetForm(CardData);
return PartialView("Form", form);
}
Above is the controller function that is being called. DriversLicenseForm is essentially the JSON object, as a C# class.
public VisitorForm GetForm(DriversLicenseForm CardData)
{
var textInfo = CultureInfo.InvariantCulture.TextInfo;
var DateOfBirth = DriversLicense.ParseDriversLicenseDate(CardData.DateOfBirthString);
CardData.FirstName = ConvertToTitleCase("i'm ron burgundy?");
CardData.LastName = textInfo.ToTitleCase(CardData.LastName);
var form = new VisitorForm();
if (DateOfBirth != null)
{
CardData.DateOfBirth = Convert.ToDateTime(DateOfBirth);
}
form = Mapper.Map<VisitorForm>(CardData);
form.ID = -1;
form = SetFormProperties(form);
return form;
}
In the above code you can see that I was getting annoyed. I decided to manually set the name and despite me setting CardData.FirstName to something completely different (verified when stepping through in debug mode) when the form comes back, it just shows my name from drivers license. I also have two title case functions because initially I thought that was the problem and found a non-textinfo solution, though that changed nothing and after debug stepping through, the data in CardData is being changed and updated properly. I feel it is worth noting one more time that the data is changed in debug mode in CardData and in the form after AutoMapper maps it out, my code appears to be working fine, except the actual view displays the original data despite there being no reference or reason for it to even know that data exists.
I literally have no idea what to do at this point.
EDIT: Here is the view code
#using (Ajax.BeginForm("PostForm", "Visitor", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "visitor-info", OnSuccess = "VisitorPostback" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { #class = "focus" })
#Html.ValidationMessageFor(model => model.LastName)
</div>
<p>
<input type="submit" class="btn btn-primary" value="Save" />
</p>
}
I cut out a lot of the actual textboxes, but left one as an example. They are all done the same way.
When sending data to a MVC Post action method, the ModelState gets populated with the data that you send. This is for instances when a form is POSTed that has validation errors, you can simply return the view back and all of the users form inputs are shown as well along with the validation errors.
Typically after a POST, you would redirect (PRG -> Post Redirect Get) to another action method, but it doesn't look like you would be able to do that here easily.
It looks like those ModelState values are being held onto the return of the "Form" view and is binding those to the matching form elements. Try adding ModelState.Clear() to your POST action method to see if that is what is causing the issue.
[HttpPost]
public ActionResult GetVisitorLogCreateVisitorForm(DriversLicenseForm CardData)
{
ModelState.Clear();
var form = visitorService.GetForm(CardData);
return PartialView("Form", form);
}

How to use Razor instead Jquery ajax to get data from a function

I never use Razor in asp.net MVC so I really don't know how it works.
Every time I want to tag a function in my Controller I use jquery ajax and I write very javascript code and my projects are very messy.
Now I want to make something different.
I have controller named as HomeController where I get data from Sql Server Database, using model entity for that.
public class HomeController : Controller
{
public JsonResult getClasses()
{
IList<Class> classes = (from x in db.Class select x).Distinct().ToList();
return Json(classes, JsonRequestBehavior.AllowGet);
}
public JsonResult getTypes(string className)
{
IList<String> allTypes = (from type in db.Type
where type.class_name == className
orderby type.type_name
select type.type_name).Distinct().ToList();
return Json(allTypes, JsonRequestBehavior.AllowGet);
}
}
And I have master.page (named as SiteMaster) where I get this data through jquery ajax.
<body>
<div id="header">
<ul class="classesName"></ul>
</div>
</body>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: "/Home/getClasses",
type: "GET",
data: {},
success: function (result) {
for (n = 0; n < result.length; n++) {
$('.classesName').append('<li><a href="#" onclick="showTypes( \'' + result[n].class_name + '\')" >' + result[n].class_name + '</a></li>');
}
}
});
</script>
I have another ajax for getting the Types when some Class is clicked..but all this I want to change so I can use #Razor.
All I want is to show the data from getClasses() into the masterPage and when some Class is clicked, to send that argument into getTypes(string className) but without using javascript and use #Razor.
So my question is can I do this and how?
The first part (sending the result of getClasses() to your view) can be done with Razor alone, because all of that work can be done on the server.
It is important to understand that the primary strength of Razor is that it allows you to perform (almost, if not) any C# on your views. So doing what you want can be as simple as writing the code as if you were coding in C#:
#{
// I'd suggest you refactor your code to have your `getClasses()` function
// readily available somewhere that's not a controller
var classes = Data.getClasses();
}
<div id="header">
<ul class="classesName">
#foreach (var clazz in classes) {
<li>
#clazz
</li>
}
</ul>
</div>
For the second part (getting the Types when the link is clicked), you're out of luck. The click has to happen on the client, so by then your C# would have been evaluated, the HTML flushed, and the time for your Razor to run would have come and gone. If those types aren't cached on the page somehow, you'll have to make a request against the server again (and AJAX is one way to do that).
You don't need to use ajax to load the initial master page, just return a view containing all the class data you need to display as that saves you the extra trip to the server. As for getting the data when a class is clicked that is an ideal place to use Ajax and to do that in Razor you would use one of the many #Ajax.xxx helpers (#Ajax.RawActionLink, #Ajax.BeginForm, etc.). The following link might be a good place to start looking at how views are used in asp.net MVC views tutorial

MVC, Jquery, and Ajax to display object based on dropdown

I'm trying to design a view to accept donations, sell memberships, or sell tickets to events.
I have a dropdown list that displays "Make a donation", "Purchase membership", and the subsequent options are populated from the IList<Event> Model passed from the controller. I use javascript to determine which panel (membership, donation, or event) should be displayed based on the selection.
The problem I'm having is that once an event is selected, I need to be able to dynamically populate the Event panel with the properties of the selected event (without, of course, having to put the user through a browser refresh). I was told by someone that I should be able to use Ajax to accomplish this. Supposedly I could go to my server/home/GetEventById action to do this. However, I haven't been able to find any examples or any tutorials that would help me accomplish this.
Could anybody shed some light on this for me by means of how to go about this, or provide examples or tutorials that would help me?
Here is a code example of fetching some content by calling a controller method through ajax, and then populating a jQuery dialog with it. Hopefully this helps point you in the right direction.
The controller method:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetItemsForJson()
{
var items = Repository.GetItems();
var result = Json(items);
return result;
}
And the jQuery to make it happen:
$('#dialog_link').click(function () {
$.getJSON("/Items/GetItemsForJson/", getItems);
});
function getItems(items) {
$("#itemlist").text("");
$.each(items, function (i, item) {
$("#itemlist").append("<li>" + item.Id + item.Name + "</li>");
});
}
Your question is a bit too broad. I assume you already implemented your Action in controller so we concentrate only on client side scripting.
Following should within $.ready:
$("#ddlSelectEvent").change(function() { // this will fire when drop down list is changed
var selection = $(this).attr("selected"); // text representation of selected value
$(".panels").hide();
$("#panel_" + selection).show(); // Assume the panel naming will be panel_MakeDonation and those...
// Now is time for ajax - load html directly
$.get("server/home/geteventbyId",
{id: "12345"},
function (data) { // callback when data is loaded
$("#panel_" + selection).html(data);
}
);
});
Above codes assume you populate content of panel with html. You might use JSON or other types depending on how you implement it.
http://docs.jquery.com/Ajax/jQuery.get#urldatacallbacktype
I'm not sure how MVC changes this, but here is how I do a callback with Ajax:
In the onchange event of the dropdownlist box you would call a java function that uses Ajax's PageMethod, something like this:
PageMethods.getVersions(LoadVersionsCallback);
The method you are calling in your .aspx.cs file has to be static, it can take parameters and looks something like:
[System.Web.Services.WebMethod]
public static string getVersions() {
StringBuilder sb = new StringBuilder();
... etc.
return sb.ToString();
}
The javascript function that you specified when you called the method will run when the method completes. It will be passed the results.
function LoadVersionsCallback(result) {
// do something with the results - I load a dropdown list box.
...etc.
}

Categories