Select dropdown value after post - asp.net-mvc-4

I was hoping for some guidance on an issue I am having with preserving the value in a dropdownlist after post (razor)
I have a simple page:
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" })
#Html.DropDownList("WardModel", Model.Wards)
<script type="text/javascript">
function ChangeHospital(val) {
window.location.href = "/PatientListByWardDD/TestDropDowns?hospID=" + val;
}
</script>
</div>
here's the controller
public ActionResult TestDropDowns(int? hospID)
{
PASInpatientRepository pasRepo = new PASInpatientRepository();
var returnModel = new ListByWardDD();
var HospitalData = pasRepo.GetPatientHospitalsEnum();
returnModel.Hospital = pasRepo.GetHopspitalListItems(HospitalData);
var WardData = pasRepo .GetPatientWardsEnum(hospID);
returnModel.Wards = pasRepo.GetWardListItems(WardData);
ViewBag.HospSearch = hospID;
return View(returnModel);
}
In the controller PASInpatientRepository() communicates with a cache database. It passes back public IEnumerable < SelectListItem > GetHopspitalListItems. It calls stored procedures written within a cache database (same as sql stored procedures in essence). This is all working fine in its own crude way.
The issue I am having is that when I select the dropdownlist #Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" }) and the controller is called to refresh the Wards dropdown, I want to preserve the value I have selected in the hospital dropdown. I have tried a few different ways, but I admit, I'm a bit stuck. Most examples I found are for strongly typed.
As I mentioned, I'm new to MVC, but any advice on how to solve this issue, or suggestions on improving my code are greatly appreciated.

So I'm not sure what the Hospital property looks like but I'll make the assumption that each one has a unique ID.
Furthermore to bind the posted data to the view model you'll need to use forms in your view. To create the drop down list use the DropDownListFor-Helper. This way the data will be bound back to your Model after submitting the form.
So your view could look something like this
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#using (Html.BeginForm("TestDropDowns", "YourController", FormMethod.Post))
{
#Html.DropDownListFor(x => x.HospitalID, Model.Hospital)
#Html.DropDownListFor(x => x.WardID, Model.Wards)
<input type="submit" value="send" />
}
</div>
Your ViewModel testContigency.Models.ListByWardDD must have at least the following properties
public class ListByWardDD {
public int HostpitalID { get;set; }
// the value of the SelectListItem-objects should be the hospital ID
public IEnumerable<SelectListItem> Hospital { get;set; }
public int WardID { get;set; }
// the value of the SelectListItem-objects should be the ward ID
public IEnumerable<SelectListItem> Wards { get;set; }
}
Once you post the form (for simplicity I added a button to send the form and left the javascript part out) the method TestDropDowns of your controller (which you need to fill in the BeginForm-Helper) will be called. That method expects expects an object of type ListByWardDD as a parameter and the framework will automatically populate the values for you.
[HttpPost]
public ActionResult TestDropDowns(ListByWardDD viewModel) {
// your code here, viewModel.HospitalID should contain the selected value
}
Note: After submitting the form the properties Hospital and Wards will be empty. If you need to display the form again, you need to repopulate those properties. Otherwise your dropdown lists are empty.
I tried my best to post valid code but I did not compile or test it.

Related

Call action method from view to automatically populate form value

I have a form which is used to create a resource, and it has two fields to be populated. When a user enters a value into the first field, I want to automatically call back to an action method on the server which will determine the value to use in the second field, without the user having to submit the form.
For example:
Full Name - User enters this value
Username - View calls server with the value specified in Full Name, server calculates value to be used, server passes value back to view, view presents the value.
Is it possible to do this in MVC core, and if so, can you please point the right direction?
I've been reading up on remote validation, and feel that I could probably use (or abuse) it in order to achieve the functionality looking for, but I'd imagine there's a property way to do this.
Any pointers appreciated.
Remote Validation can only do the validation without submitting the form, but It can't assign value to another field. In your case. It's actually very simple. You can use the js onchange event listen to the first field, in the event, use ajax to access the background. and then fill the returned value into the second field in the callback function. Below is a simple test
View:
<span>Full Name</span>
<input type="text" id="FullName" name="FullName" />
<span>User Name</span>
<input type="text" id="UserName" name="UserName" />
#section scripts{
<script>
$("#FullName").on("change", function () {
$.ajax({
type: "post",
url: "/User/GetUserName",
data: {
fullname: $("#FullName").val()
},
success: function (result) {
$("#UserName").val(result);
}
})
})
</script>
}
Controller:
[HttpPost]
public string GetUserName(string fullname)
{
var username = fullname.Split(" ");
return username.First();
}
Result:
#mj1313's answer was extremely helpful, but I didnt like the idea of having some loose Javascript kicking around in my views, especially since I may need to use this same functionality in multiple views.
I ended up converting the script into a global function, and calling it from within the onchange event like so.
JS Function
(in site.js)
function fullNameToUserName (fullNameId, usernameId) {
$.ajax({
type: "get", // GET rather than POST
url: "/User/GetUserNameFromFullName",
data: {
fullname: $(fullNameId).val()
},
success: function (result) {
$(usernameId).val(result);
}
})
}
Action method
[HttpGet("GetUserNameFromFullName")] // GET rather than POST
public IActionResult GetUserNameFromFullName(string fullName)
{
var username = fullName.Split(" ");
return Ok(username.First());
}
Model
public class UserModel
{
public string FullName { get; set; }
public string Username { get; set; }
}
View
#model MvcApp.Models.UserModel
#Html.DisplayNameFor(m => m.FullName)
#Html.TextBoxFor(m => m.FullName, new
{
onchange = $"fullNameToUserName({#Html.IdFor(m => m.FullName)}, {#Html.IdFor(m => m.Username)});"
})
#Html.DisplayNameFor(m => m.Username)
#Html.TextBoxFor(m => m.Username)
The only problem I have with this approach is that the call to fullNameToUserName(fullNameId, usernameId) is not strongly-typed, and if this function is called from multiple views, it's likely to be miss-typed at some point.
While #mj1313's answer was great and pointed me in the right direction, I wasn't fully satisfied with the approach. Personally, I prefer this approach as it's slightly more reusable and keeps my view's more lean.

kendo editor not responding after multiple requests to the same page in IE

I have a very weird bug. I have a page on MVC that displays two editors and gets passed a model with the value for both editors. The model is as follows:
public class BulletinsModel
{
[AllowHtml]
[Display(Name = "Some Bulletin")]
public string SomeBulletin { get; set; }
[AllowHtml]
[Display(Name = "Other Bulletin")]
public string OtherBulletin { get; set; }
}
I then, defined a view which receives this view model and maps it to two kendo editors.There is also some javascript code to make a post to update the information.
#model BulletinsModel
<div id="settings">
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(m => m.SomeBulletin, new { #class = "col-md-6 text-left" })
#(Html.Kendo().EditorFor(m => m.SomeBulletin).Encode(false).Name("Some_Bulletin"))
#Html.LabelFor(m => m.OtherBulletin, new { #class = "col-md-6 text-left" })
#(Html.Kendo().EditorFor(m => m.OtherBulletin).Encode(false).Name("Other_Bulletin"))
</div>
</div>
</div>
My code for my action method that renders this view is as follows (nothing fancy):
[HttpGet]
public PartialViewResult Index()
{
ViewBag.ActiveSectionName = "Bulletins";
var bulletinModel = GetBulletinsModel();
return PartialView("_Bulletins",bulletinModel);
}
However, my issue is that after hitting the Index action a couple of times, the editors become non responsive and I cannot edit the information on them. This only happens on IE, as I have not been able to replicate the issue in other browsers.
EDIT: I have just noticed that the editor is frozen. In order to be able to edit what's inside of the editor I need to click on any option of the toolbar to make it responsive once again. Why is that?
Turns out that the issue is happening with IE as detailed in this post:
Adding, removing, adding editor -> all editors on page become read only in IE. The problem is with the iframes inside the editor. I was loading my page with an Ajax request to which I had to add the following code before making the request to make it work.
function unloadEditor($editor) {
if ($editor.length > 0) {
$editor.data('kendoEditor').wrapper.find("iframe").remove();
$editor.data('kendoEditor').destroy();
}
}
unloadEditor($('#myEditor'));

Post back Errors in MVC 4

In regular Asp.Net, when you want to post an error, for ex, a duplicate ID, I use Panels for controls, error message labels, etc. I was wondering, how to achieve the same using MVC 4.
Currently, on my Index page, I have an Id, Name & Address column and a Submit button. My HTTPPOST ActionResult here:
[HttpPost]
public ActionResult Index(Person p)
{
if (ModelState.IsValid)
{
PersonInfo pi = new PersonInfo();
var duplicate = from d in db.PersonInfoes
where d.Id == p.Id
select d;
if (duplicate.Any())
{
return View("Duplicate");
}
else
{....}
When I enter a duplicate Id, upon submitting the page, I need to post to the same view ("Index") page to let user know that there is an Id already, but currently, I am redirecting to a different view to let user know as a duplicate, and I dont think this is the right way to do this. I am learning MVC, btw and hence this doubt. My duplicate cshtml here:
#{
ViewBag.Title = "Duplicate";
}
<h2>Duplicate</h2>
<h3>Duplicate ID found !!</h3>
<p>Please correct the ID and re-enter !</p>
<br/><br/>
#Html.ActionLink("Back to Data Entry","Index")
Any pointers would help.
You can return to the same view. You should consider adding an error to the model state dictionary so that you can show that in the UI.
[HttpPost]
public ActionResult Index(Person p)
{
if (ModelState.IsValid)
{
var duplicate= db.PersonInfoes.Where(s=>s.Id== p.Id);
if (duplicate.Any())
{
ModelState.AddModelError(string.Empty,"Duplicate found");
return View(p);
}
// to do : Your existing code
}
return View(p);
}
Just make sure you are calling the ValidationSummary method in your Index view to show this error message.
#model Person
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
<!-- Your form fields goes here-->
<input type="submit" />
}

Passing textbox value from View to action without using Html.begin form and Submit button

I have created a texbox. When user give some input in the textbox and click the actionlink below, the value of the textbox will get pass to the actionResult(FWMenu) in the controller. I can not use html.begin form and submit button in the view. And i can not even use [httppost] in my controller.
Is it possible in that way? If yes then please help me how.
I have not used any class in model.
Below is my Controller.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult FWMenu(string username)
{
return View();
}
}
This is my View.
<div>
#Html.TextBox("txtUserName")
#Html.ActionLink("Login", "FWMenu", new { username = #Html.TextBox("txtUserName") })
</div>
You need to use javascript/jquery to build the url and redirect. From your comments you mentioned you wanted to use a image rather than a button or link, and that you will have multiple items, so assuming you html is
<div>
<input type="text" name="username">
<img class="submit scr=....>
<div>
<script>
var urlBase='#Url.Action("FWMenu");
$('.submit').click(function() {
var userName = $(this).prev('input').val();
location.href = urlBase + '/' + userName;
}
</script>
Side note: No real point using #Html.TextBox("txtUserName") and if you have multiple instance of this it would generate invalid html (duplicate id attributes) and in any case the name of the parameter is username so it would have needed to be #Html.TextBox("username")`

ASP.NET MVC How to Use two Actionresults with Html.BeginForm?

I'm trying to do the same as this ASP.NET MVC Using two inputs with Html.BeginForm question describes but with enough difference that I don't really know hwo to apply it on my project:
I have a view that has 3 dropdownlists(profilelist, connected salarylist & not connected salarylist)
Looks like this:
<div class="row bgwhite">
#using (Html.BeginForm("GetConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "ProfileListForm" }))
{
<div class="four columns list list1">
#Html.DropDownList("Profiles", (SelectList) ViewBag.Profiles, "--Välj profilgrupp--",
new
{
//onchange = "$('#ProfileListForm')[0].submit();"
// Submits everytime a new element in the list is chosen
onchange = "document.getElementById('ProfileListForm').submit();"
})
</div>
}
#using (Html.BeginForm("Index", "KumaAdmin", FormMethod.Get, new { Id = "SalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList) ViewBag.Salaries, "--Kopplade LöneGrupper--")
</div>
}
#using (Html.BeginForm("GetNOTConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "NotConSalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList)ViewBag.NotConSalaries, "--Ej Kopplade LöneGrupper--")
<input style="float: left;" type="submit" value="Knyt" />
</div>
}
</div>
as you can see above when i change an element i the profile list i have script code that submits the form and calls the following actionresult that populates my "connected salarylist".
[HttpGet]
public ActionResult GetConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
What I wan't to do:
When I chose a element in the profilelist i would like to call 2 actionresults, the one that i have shown above AND a second one that will populare my third list that will contain "not connected salaries".
Second Actionresult:
public ActionResult GetNOTConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
I don't want to do this with AJAX/JSON, strictly MVC.
I read the question that i linked above but did not know how to apply it to my project or if it is even possible to do the same.
If more info is needed ask and i will do my best to provide it.
Thank you!
I was so sure that the best way to do this was to have two actionresults that i was totaly blinded to the soloution that i could call both my db methods from the same actionresult and populate both of the lists.
Simple soloution:
[HttpGet]
public ActionResult GetSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
Model.NotConSalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
Sorry if I wasted your time:( but hopefully this will help others that attempt the same.
However if there is a way to do this in two actionresults then I will leave the question as open, would be interesting to see how it is done.