Working with Multiple forms in MVC4 - asp.net-mvc-4

I am creating a small application in MVC 4. I have 2 forms form1 and form2 in single view. When one form is posted I proceed to the next form where I redirect from view to a action method Employee in which I perform some task and then again from action method I return back to view, in the view I want to go for form2 without touching form1 how do I do that.
Below I will show sample of how my code looks like
Controller
public ActionResult Index()
{
//some task
resturn view();
}
public ActionResult Employee()
{
//some task
}
View
#html.BeginForm("Index","Show",FormMethod.Post,new {#name=form1})
{
//Shown something
}
#Html.Action("Employee","Show")
#html.BeginForm("Employee","Show",FormMethod.Post,new {#name=form2})
{
//Shown something
}

Create a Javascript that checks if any of the required values in form 1 is set. If so, set focus on whatever element you want in form 2. Here's an example:
The html forms:
<form id="one">
<input type="text" id="requiredFormField" value="some value previously entered" />
</form>
<form id="two">
<input type="text" id="form2text" />
</form>
And the javascript:
$(document).ready(setFocusIfForm1Completed());
function setFocusIfForm1Completed()
{
var expectedValue = $("#requiredFormField").val();
if(expectedValue.length > 0)
{
$("#form2text").focus();
}
}
I should add that I assume that you fill in form 1, then you submit (do a POST request to the server) which does some work, and then returns the same view but with at least some required value from form 1 populated (otherwise, this solution breaks).

Related

Blazor: Call a server method when a button is clicked?

I created a sample Blazor project. The scaffolding has two examples, (1)calling a C# method that exists IN the web page = counter, (2)calling a server method at the BEGINNING of the web page initialisation = weather table. But not any example for calling a server method and get result when a button is clicked. Is that possible?
For example, can I add a method like this to the "WeathrForecastService.cs":
public int Add(int a, int b)
{
return a + b;
}
and like the "counter" example, add a button on the page, and when the button is clicked, display the result of it:
#page "/fetchdata"
#using BlazorApp1.Data
#inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
#if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<p>Current count: #[display the result of ForecastService.Add(1,2)]</p>
<button class="btn btn-primary" #onclick="[call ForecastService.Add(1,2)]">Click me</button>
}
#code {
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
calling a server method and get result when a button is clicked. Is that possible?
Yes. If you're using Blazor Server Side, the server side method will be invoked via SignalR under the hood.
Similar to the way we do in Angular/React, in order to display the result, we need create a _sum field to cache the result so that we can display/change it later:
#code {
private int _sum;
}
And change your onclick as below:
<p>Current count: #this._sum</p>
<button class="btn btn-primary" #onclick="()=>this._sum= ForecastService.Add(1, 2)" >Click me</button>

using must in fluentValidation

I am using FluentValidation for the server side validation. Now I want to call a function using must.
This is the form code snippet :
<form method="post"
asp-controller="Category"
asp-action="SaveSpecification"
role="form"
data-ajax="true"
data-ajax-loading="#Progress"
data-ajax-success="Specification_JsMethod">
<input asp-for="Caption" class="form-control" />
<input type="hidden" asp-for="CategoryId" />
<button class="btn btn-primary" type="submit"></button>
</form>
What changes should I make to the code below to call function SpecificationMustBeUnique ?
public class SpecificationValidator : AbstractValidator<Specification>
{
public SpecificationValidator()
{
RuleFor(x => new { x.CategoryId, x.Caption}).Must(x => SpecificationMustBeUnique(x.CategoryId, x.Caption)).WithMessage("not unique");
}
private bool SpecificationMustBeUnique(int categoryId, string caption)
{
return true / false;
}
}
Tips: 1 - The combination of CategoyId and Caption should be unique
2 - Validation is not done when submitting the form(the validation just not running when submit the form)
The tricky part is deciding which property should be validated when the validation rule applies to a combination of values on different fields. I usually just close my eyes, and point to one of the view model properties and say "this is the property I'll attach the validator to." With very little thought. FluentValidation works best when the validation rules apply to a single property, so it knows which property will display the validation message.
So, just pick CategoryId or Caption and attach the validator to it:
RuleFor(x => x.CategoryId)
.Must(BeUniqueCategoryAndCaption)
.WithMessage("{PropertyName} and Caption must be unique.");
The signature for the BeUniqueCategoryAndCaption method would look like:
private bool BeUniqueCategoryAndCaption(Specification model, int categoryId)
{
return true / false;
}
Note: I guessed that the CategoryId property is an int, but you will need to make sure the categoryId argument to BeUniqueCategoryAndCaption is the same type as the CategoryId property in your view model.

Select dropdown value after post

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.

Pass data from view to controller method

I need to use the name of the current view in a method in my controller
I am able to get the name with the code below in my view.
I will like to pass this #ViewData["pageName"] to my MakeChange action result
in my controller. Each time I step through the MakeChange method all I get
is "object reference not set to an instance of an object"
How can I pass data from my view to controller method ?
#ViewData["pageName"] = #Path.GetFileName(Server.MapPath(VirtualPath))
public ActionResult MakeChange(string lang)
{
string getPageName = ViewData["pageName"].ToString();
return RedirectToAction(getPageName, "Home");
}
You can't pass data from view to controller using ViewData. You can use ViewData to pass data from Controller to your view.
To achieve what you want, you can do as follows:
<input type='hidden' name='lang' value='#Path.GetFileName(Server.MapPath(VirtualPath))' />
<input type='submit' value='send'>
Ps: you should put the input's inside a form tag.
Path.GetFileName(Server.MapPath(VirtualPath)) will give you the razor view name with extension (Ex : index.cshtml). You can not use that with RedirectToAction method as RedirectToAction method is looking for an action method name. You need to trim down the file extension part before using it.
To send this to the controller action, you can keep the value inside a hidden field inside your form. When user posts the form, it will be available in your HttpPost action method. You need to make sure that there is a parameter which has same name as the hidden field's name value.
#using (Html.BeginForm())
{
<input type="text" name="lang" value="English" />
<input type="hidden" name="pageName"
value="#Path.GetFileName(Server.MapPath(VirtualPath))" />
<input id="BtnAdd" type="submit" value="Save" />
}
So your action method will be
public ActionResult MakeChange(string lang,string pageName)
{
var viewName=pageName;
//Get rid of the extension.
viewName = viewName.Replace(".cshtml","");
return RedirectToAction(viewName , "Home");
}
Even if you are doing an ajax post, it will still work, just serialize your form and send it
$("#BtnAdd").click(function(e){
e.preventDefault();
var _this = $(this);
$.post("#Url.Action("MakeChange","Home")",_this.closest("form").serialize(),
function(response){
//do something with response
});
});
There are more clean ways of getting the view name without the file extension trimming approach we did. Take a look at this answer.

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")`