Passing selected string from dropdown to controller - c#

I am displaying a dropdown list from my controller as follows:
public ActionResult Index()
{
var title = new List<String>()
{
"English", "French", "Spanish"
};
List<SelectListItem> languageList = new List<SelectListItem>();
string defaultTitle = "Language";
foreach (var item in title)
{
languageList.Add(new SelectListItem()
{
Text = item,
Value = item,
Selected = (item == defaultTitle ? true : false)
});
}
ViewBag.LanguageList = languageList;
return View();
}
My View is as follows:
#using (Html.BeginForm("GetStatusTrad", "StatusTradController", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Translation</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Language")
#Html.DropDownList("lang", new SelectList(ViewBag.LanguageList, "Text", "Value"), "Language")
</div>
</div>
<div></div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
It displays the dropdown correctly, but when I want to pass the selected value to an action of the controller, I get a 404 error.
My action is :
public ActionResult GetStatusTrad(string language)
{
*some code*
}
Any idea why I can't pass the selected value of the dropdown to the controller?

Your helper should be:
#Html.DropDownList("language", <-- note this
new SelectList(ViewBag.LanguageList, "Text", "Value"), "Language")
It happend becose basically your helper will be rendered to input like this:
<select name="language">
...
</select>
And on form POST your controller will be able to bind your values based on name property of input.

#Html.DropDownList("lang", new SelectList(... generates a <select> with name="lang. You need to change the parameter in the POST method to match
public ActionResult GetStatusTrad(string lang)
As a side note, LanguageList is already IEnumerable<SelectListItem> so using new SelectList() to create another identical one is pointless. I can be just
#Html.DropDownList("lang", (IEnumerable<SelectListItem>)ViewBag.LanguageList, "Language")
You also have a typo in the BeginForm() method. It needs to be
#using (Html.BeginForm("GetStatusTrad", "StatusTrad", FormMethod.Post))
i.e. "StatusTrad", not "StatusTradController" (unless you have really named your controller StatusTradControllerController)

you can use strongly type view to return selected dropdown value.
create simple class like below
public class myModel
{
public string language { get; set; }
....
....
}
Then use this class/model in View
#model yourProject.Models.myModel
<div class="form-group">
<label class="col-lg-2 control-label">Language</label>
<div class="col-lg-5">
#Html.ValidationMessageFor(m => m.language)
#Html.DropDownListFor(m => m.language, new SelectList(ViewBag.LanguageList, "Text", "Value"), "-- Select --", new { #class = "form-control" })
</div>
</div>
Controller method look like below
[HttpPost]
public ActionResult GetStatusTrad(myModel model)
{
*some code*
}

Related

How to handle multiple dynamic form with c#

I want to have a page on my website where I can upload files. For each file I want to have a name and a category.
[Required(ErrorMessage = "Please choose a file")]
[Display(Name = "File")]
public HttpPostedFileBase file { get; set; }
[Required(ErrorMessage = "A name is required")]
[Display(Name = "Name")]
public string name { get; set; }
[Display(Name ="Category")]
public string cat { get; set; }
This is my model. I want to have some dynamic form, what I mean is a form with a button that allows the user to add another form on the page to upload multiple files with a name and a category for each file. I've done this with Symfony2, but I have no idea how to do it with ASP.NET. Can someone help me please ?
At first create another model like following:
public class fileListModel{
IList<yourModel> fileList {get;set;}
}
Then in the razor view create form like this way:
#model fileListModel
<form>
//dynamic html(you can also use partial for this). When button will be clicked append following html using jquery $(form).append()
#{var key = [use some random id or guid]}
<input type="hidden" name="fileList.Index" value="#key" />
<input type="text" name="fileList[#key].name" value="Name" />
<input type="text" name="fileList[#key].cate" value="Category" />
<input type="file" name="fileList[#key].file" value="Upload"/>
// end dynamic html
#{ key = [use some random id or guid]}
<input type="hidden" name="fileList.Index" value="#key" />
<input type="text" name="fileList[#key].name" value="Name" />
<input type="text" name="fileList[#key].cate" value="Category" />
<input type="file" name="fileList[#key].file" value="Upload"/>
// end dynamic html
</form>
Now create a controller action method to accept the fileList:
public ActionResult upload(fileListModel fileList){
//save them to db
}
The following is a bare minimum example based on this blogpost. For demo purposes, I've named my model Foo. So whenever you read this, this should be your model with file, name and cat properties.
First, add https://www.nuget.org/packages/BeginCollectionItem/ to your project.
Then, add a partial view to your Views folder. I've named mine "_AddFile.cshtml":
#model WebApplication2.Models.Foo
#using (Html.BeginCollectionItem("files"))
{
<div class="form-horizontal">
<fieldset>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
</div>
#Html.LabelFor(model => model.Cat, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Cat, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
</fieldset>
</div>
}
Note, the Html.BeginCollectionItem("files"), this is creating a collection, that is later grouped together and bound to your model named "files".
Our controller looks like this:
public ActionResult Index()
{
//Initialize the view with an empty default entry
var vm = new List<Foo> {
new Models.Foo {
Cat ="foo",
Name =" bar"
}
};
return View(vm);
}
//this calls your partial view and initializes an empty model
public PartialViewResult AddFile()
{
return PartialView("_AddFile", new Foo());
}
//note "files" name? The same as our collection name specified earlier
[HttpPost]
public ActionResult PostFiles(IEnumerable<Foo> files)
{
//do whatever you want with your posted model here
return View();
}
In your view, use this form:
#model IEnumerable<WebApplication2.Models.Foo>
#using (Html.BeginForm("PostFiles","Home", FormMethod.Post))
{
<div id="FileEditor">
#foreach (var item in Model)
{
Html.RenderPartial("_AddFile", item);
}
</div>
<div>
#Html.ActionLink("Add File", "AddFile", null, new { id = "addFile" }) <input type="submit" value="Finished" />
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script>
$(function () {
$("#addFile").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#FileEditor").append(html); }
});
return false;
});
})
</script>
}
The foreach loop renders a partial View for each model entry, in our case just one with a default entry.
The javascript loop then calls our PartialView and renders an empty template below the existing ones.
A call to submit, then lets you deal with your files in any way you want:

How can I pass string value for "asp-for" in asp net 5

I want to write a Edit.cshtml file for an entity with many properties to edit, so I have to write the following codes many times:
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
Actually, there are many entities so that I have to write many Edit.cshtml files. I want to make some simplifications
I want to select some properties of the entity in the controller and use loop to show the properties in the view. For example:
In the controller file:
public IActionResult Edit(string id)
{
var model = GetModel(id);
var propertyNames= new List<string>()
{
"Name",
"Email"
// add some other property names of the entity
};
ViewData["PropertyList"] = propertyNames;
return View(model);
}
In the view file:
#{
var propertyNames = (List<string>)ViewData["PropertyList"];
foreach (string item in propertyNames)
{
<div class="form-group">
<label asp-for="#(item)" class="col-md-2 control-label"></label>
<div class="col-md-3">
<input asp-for="#(item)" class="form-control" />
<span asp-validation-for="#(item)" class="text-danger"></span>
</div>
</div>
}
}
but it cannot work, since it generates wrong codes. It seems that I cannot pass a string value for "asp-for" tag helper.
For example, if I change the code of top to this:
#{
string e = "Email";
<div class="form-group">
<label asp-for="#e" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="#e" class="form-control" />
<span asp-validation-for="#e" class="text-danger"></span>
</div>
</div>
}
The code above will generate this:
<div class="form-group">
<label class="col-md-2 control-label" for="e">e</label>
<div class="col-md-10">
<input class="form-control" type="text" id="e" name="e" value="Email" />
<span class="text-danger field-validation-valid" data-valmsg-for="e" data-valmsg-replace="true"></span>
</div>
</div>
The expected code is:
<div class="form-group">
<label class="col-md-2 control-label" for="Email">Email</label>
<div class="col-md-10">
<input class="form-control" type="email" data-val="true" data-val-email="Email 字段不是有效的电子邮件地址。" data-val-required="Email 字段是必需的。" id="Email" name="Email" value="" />
<span class="text-danger field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"></span>
</div>
</div>
How should I do?
Is it possible in razor?
Ok, I managed to get this working. DISCLAIMER: It is super hacky and I have no idea if I've done it in the best way possible. All I know is that it does what you want and it might point you in the right direction.
Firstly, I created a model:
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class TestModel
{
[Required]
public string Name { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email Address")]
public string Email { get; set; }
}
}
Then, I made a custom tag helper. This is the horrible bit where the "magic" happens. Specifically the first section of the Process method...
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Razor.TagHelpers;
using System.Linq;
namespace WebApplication1.TagHelpers
{
[HtmlTargetElement("edit")]
public class EditTagHelper : TagHelper
{
[HtmlAttributeName("asp-for")]
public ModelExpression aspFor { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
protected IHtmlGenerator _generator { get; set; }
public EditTagHelper(IHtmlGenerator generator)
{
_generator = generator;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var propName = aspFor.ModelExplorer.Model.ToString();
var modelExProp = aspFor.ModelExplorer.Container.Properties.Single(x => x.Metadata.PropertyName.Equals(propName));
var propValue = modelExProp.Model;
var propEditFormatString = modelExProp.Metadata.EditFormatString;
var label = _generator.GenerateLabel(ViewContext, aspFor.ModelExplorer,
propName, propName, new { #class = "col-md-2 control-label", #type = "email" });
var input = _generator.GenerateTextBox(ViewContext, aspFor.ModelExplorer,
propName, propValue, propEditFormatString, new { #class = "form-control" });
var validation = _generator.GenerateValidationMessage(ViewContext, aspFor.ModelExplorer,
propName, string.Empty, string.Empty, new { #class = "text-danger" });
var inputParent = new TagBuilder("div");
inputParent.AddCssClass("col-md-10");
inputParent.InnerHtml.Append(input);
inputParent.InnerHtml.Append(validation);
var parent = new TagBuilder("div");
parent.AddCssClass("form-group");
parent.InnerHtml.Append(label);
parent.InnerHtml.Append(inputParent);
output.Content.SetContent(parent);
base.Process(context, output);
}
}
}
NB: To make the custom TagHelper work, you need to add a line into the _ViewImports.cshtml file, like this (replace WebApplication1 with your namespace):
#addTagHelper "*, WebApplication1"
I changed my action to this, to sort of match yours (maybe you can use reflection to get your model property names here?):
public IActionResult Index()
{
var propertyNames = new List<string>()
{
"Name",
"Email"
};
ViewData["PropertyList"] = propertyNames;
var m = new TestModel()
{
Name = "huoshan12345",
Email = "test#test.net"
};
return View(m);
}
Then finally, in the view, you can do something like this:
<div class="row">
#using (Html.BeginForm())
{
var propertyNames = (List<string>)ViewData["PropertyList"];
foreach (string item in propertyNames)
{
<edit asp-for="#item"></edit>
}
<input type="submit" value="Submit" />
}
</div>
You can also try this:
foreach (string item in propertyNames){
#Html.TextBox(item, value: null, htmlAttributes: new { #class = "form-control" })
}
Yes, it is possible to write it with razor.
think this will help you. If you don't put the "type" attribute it will generate it with type='text'. You can also hard code your data-val attributes, but it is not recommended just replace the '-' with '_' Ex: #data_val_email
<div class="form-group">
#Html.LabelFor(x=>x.Email)
<div class="col-md-10">
#Html.TextBoxFor(x => x.Email, new { #class = "form-control", #type = "email" })
#Html.ValidateFor(x=>x.Email)
</div>
</div>
Here's a related technique. I extended the tag helper to inject the required HTML into the page. This works a bit like the ASP.NET MVC EditorTemplate.
Here's the custom tag helper that injects a special partial view
public class MyFormGroupTagHelper : PartialTagHelper
{
public MyFormGroupTagHelper(ICompositeViewEngine viewEngine, IViewBufferScope viewBufferScope) : base(viewEngine, viewBufferScope)
{ }
public ModelExpression Property { get; set; }
public string LabelText { get; set; } = null;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
this.For = Property;
this.Name = "_FormGroup";
// Have to use Viewdata to pass information to the partial view, because the model is the single property of the entity that will be posted back to the controller
this.ViewData["TH_LabelText"] = LabelText;
this.ViewData["TH_DataTypeName"] = Property.Metadata.DataTypeName;
await base.ProcessAsync(context, output);
}
}
Here's the partial view _FormGroup.cshtml. This generates the markup for a single field on a form with the Bootstrap styles. It also attaches the "datepicker" class to the input tag if the field is a date. Notice how asp-for is populated with #Model so this same view can be bound to any property on any entity model
#model object
#{
string LabelText = (string)#ViewData["TH_LabelText"];
string DataTypeName = (string) ViewData["TH_DataTypeName"];
bool IsDate = (DataTypeName == "Date");
}
<div class="form-group row">
#if (LabelText != null)
{
<label asp-for="#Model" class="control-label col-md-4">#LabelText</label>
}
<div class="col-md-8">
<input asp-for="#Model" class="form-control #( IsDate ? "datepicker" : "")" />
<span asp-validation-for="#Model" class="text-danger"></span>
</div>
</div>
Now in the Create or Edit view, where the model is the business entity, you can create the code to edit a single property on the entity like this.
<my-form-group label-text="Result date" property="ResultDate" view-data="ViewData" ></my-form-group>
In this case the model is a class with a field called ResultDate defined like this:
[DataType(DataType.Date)]
public DateTime? ResultDate { get; set; }
Note that you must set the view-data attribute to pass the ViewData object into tag helper. It uses ViewData to pass information on to the partial view. This isn't ideal but I couldn't think of any other way to pass information through to the partial view. I prefixed the keys with "TH_" to prevent the tag helper overwriting any other values in Viewdata.

How to solve conflict between two models, one in template and other in a View in ASP.NET MVC

I am having problems when I have a model in my navigation partial and a model in my registration view (which of course is in #RenderBody()
This is the error I am getting
The model item passed into the dictionary is of type 'ProfessionalDev.Models.Registration', but this dictionary requires a model item of type 'ProfessionalDev.Models.Login'.
Line 14: <body>
Line 15:
Line 16: #Html.Partial("_Navigation") <=== THE ERROR IS HERE
Line 17: <div class="top-bg"></div>
Line 18: <div class="container body-content">
My layout if as follows
#Html.Partial("_Navigation")
<div class="top-bg"></div>
<div class="container body-content">
#Html.Partial("_Header")
<main>
#RenderBody()
</main>
<footer>
<p>Copyright © 2009-#DateTime.Now.Year Company</p>
</footer>
</div>
The _Navigation partial where I have a Login model because of the Login form
#model ProfessionalDev.Models.Login
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
...
<ul class="dropdown-menu">
<li>
#Html.Partial("_Messages")
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.ValidationMessageFor(model => model.Email)
#Html.TextBoxFor(model => model.Email, new { #class = "form-control", #placeholder = "Email" })
#Html.ValidationMessageFor(model => model.Passwd)
#Html.PasswordFor(model => model.Passwd, new { #class = "form-control", #placeholder = "Password" })
<input type="submit" value="Submit" name="action:Login" class="btn btn-primary btn-sm" />
}
</li>
<li role="separator" class="divider"></li>
<li>Forgot Password?</li>
</ul>
...
Then I have the Registration view with a Registration model
#model ProfessionalDev.Models.Registration
#{
ProfessionalDev.Library.IdentifierTypeItems identifierTypes = new ProfessionalDev.Library.IdentifierTypeItems();
}
<h1>New User Account</h1>
<hr />
#using (Html.BeginForm("Index", "Registration", FormMethod.Post)) {
<div class="row">
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(model => model.FirstName, "First Name", new { #class = "control-label" }) <span class="required">* </span>
#Html.ValidationMessageFor(model => model.FirstName)
#Html.TextBoxFor(model => model.FirstName, new { #class = "form-control" })
</div>
...
<input type="submit" value="Register" name="action:Register" class="btn btn-primary" />
}
RegistrationController
public class RegistrationController : Controller
{
// GET: Registration
public ActionResult Index()
{
return View();
}
[HttpPost]
[MultipleButton(Name = "action", Argument = "Register")]
public ActionResult Index(Registration reg) {
if (ModelState.IsValid) {
...
return RedirectToAction("Index", "Home");
}
return View("Index", reg);
}
}
When I click the Register button and the Registration object is invalid, as you can see above is supposed to return reg to the Registration/Index view, but instead is returning the view to the Home/Index view from the _Navigation partial
HomeController
public class HomeController : Controller
{
[HttpPost]
[MultipleButton(Name = "action", Argument = "Login")]
public ActionResult Index(Login login)
{
...
return View("Index", login);
}
(I hope I am making sense...)
How can I fix this?
This is how it looks from GUI. The Login in the top menu and the register page
You get redirected to because you using #using (Html.BeginForm("Index", "Home", FormMethod.Post)) inside your main view and doesn't matter what is inside your partial view.
Besides, Only way you could call partial view method inside a main view is jQuery.
The best approach is using a ProfessionalDev.Models.Login type property inside your ProfessionalDev.Models.Registration.
Then use your Html.BeginForm for login inside the registration view.

Partial views and html forms Asp Mvc

I am attempting to render a partial view that contains a simple html form. I want to render the form from a controller as I handle the postback from an overloaded controller method. I have tried #Html.Action("ContactForm")but I get an Exception because child actions cannot redirect.
My Controller:
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult ContactForm()
{
return PartialView(new ContactForm());
}
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return RedirectToAction("FormResults", new { ContactForm = Contact });
}
public ActionResult FormResults(ContactForm Contact)
{
return PartialView(Contact);
}
My Form:
#using(Html.BeginForm())
{
<h2>Contact Form</h2>
<div class="input-group">
<h4>#Html.LabelFor(m => m.FirstName, "First Name")</h4>
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control", #placeholder = "First Name" })
</div>
<div class="input-group">
<h4>#Html.LabelFor(m => m.LastName, "Last Name")</h4>
#Html.TextBoxFor(m => m.LastName, new { #class = "form-control", #placeholder = "Last Name" })
</div>
<div class="input-group">
<h4>#Html.LabelFor(m => m.Email, "Email")</h4>
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", #placeholder = "Email", #type = "text" })
</div>
<input type="submit" class="btn btn-info" value="Submit" />
}
Any Help on how I would accomplish this would be appreciated.
try surrounding the form with a div and a certain id and use:
#using(Ajax.BeginForm("ContactForm","YourController",new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "yourCertainId",
HTTPMethod = "POST"
})
and your ActionMethod:
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return Partial("YourPartialName", Contact });
}
make sure that you include the bundle jqueryval on the bottom of your view.
you wont need the second controller method "FormResults"
Does something like this not work for you?
I don't think you need a redirect.
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return PartialView("FormResults", Contact);
}
This uses the
PartialView(string viewName, object model)
overload of the PartialView method in the Controller class.
This allows you to use a View that doesn't match the ActionResult's method name.
The same thing works for the plain "View" method as well.

Retrieve Value Partial View and Pass to Controller

I have a partial view that allows a user to select a customer. When the user selects the customer they will clock on the LoadConfiguration button in the view.
I want the view to pass the selected customer to the controller action method so that I can use it in my logic when loading the files.
Can someone advise the best way to do this my code so far is below:
Partial View
#model Mojito.Models.Customer
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.CustomerId, "Customer", new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.DropDownList("CustomerId", String.Empty)
#Html.ValidationMessageFor(model => model.CustomerId)
</div>
</div>
</div>
}
View
#using Mojito.Models
#model Mojito.Models.MojitoXmlConfiguration
#{
ViewBag.Title = "Mojito Load Config";
}
<div>#Html.Partial("~/Views/Shared/_Customer.cshtml")</div>
#using (Html.BeginForm("Load", "MojitoXmlConfiguration", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" value="Load Mojito Configuration" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
using System.Web.Mvc;
using Mojito.Models;
namespace Mojito.Controllers
{
public class MojitoXmlConfigurationController : Controller
{
private MojitoContext _db = new MojitoContext();
//
// GET: /MojitoXmlConfiguration/
public ActionResult Index()
{
ViewBag.CustomerId = new SelectList(_db.Customers, "CustomerId", "CustomerName");
return View();
}
[HttpPost]
public ActionResult Load()
{
var mojitoXml = new MojitoXmlConfiguration.Importer(#"C:\Users\Documents\XML Files\SampleList");
mojitoXml.ImportWsaHolidayUsingXElement();
ViewBag.CustomerId = new SelectList(_db.Customers, "CustomerId", "CustomerName");
return View("Index");
}
}
}
Based on your setup, it looks like your partial view will POST back to the Index action. I would add another action method like so
[HttpPost]
public ActionResult Index(int CustomerId)
{
//process
Return View("Load")
}

Categories