MVC textbox list - asp.net-mvc

I want my mvc to act the same way as my classic asp example. My classic asp example is simple.
I can add as many values to list box with np. my mvc only allows one then replace each value everytime I add one. How can I get my mvc to work like Classic asp.net.
Classic Asp.net
aspx.
<asp:textbox runat="server" ID="StoreToAdd" ></asp:textbox>
<asp:Button ID="btnAddStore" runat="server" Text="Add" OnClick="btnAddStore_Click1" />
backend c#
protected void btnAddStore_Click1(object sender, EventArgs e)
{
lstStores.Items.Add(StoreToAdd.Text);
StoreToAdd.Text = "";
}
MVC view
#using(Html.BeginForm("Index", "Home")) {
#Html.TextBoxFor(mod => mod.StoreToAdd, new { Style = "height:20px; " })
<div align="center">
<input type="submit" name="addS" id="addS" value="Add"
/></div>
#Html.ListBoxFor(model => model.lstStores, new
new MultiSelectList(Model.lstStores),
new { style = "width:225px;height:255px" })
}
Controller
[HttpPost]
public ActionResult Index(HomeModel model)
{
model.lstStores = new List<string>();
model.lstStores.Add(model.StoreToAdd);
return View(model);
}

Your doing this in your controller:
model.LstStores = new List<string>();
Wouldn't you expect that to reset your list each time?
Perhaps you could check if the list has already been instantiated first, and just add the new item if it has:
[HttpPost]
public ActionResult Index(HomeModel model)
{
if (model.lstStores != null)
{
model.lstStores.Add(model.StoreToAdd);
}
else
{
model.lstStores = new List<string>();
model.lstStores.Add(model.StoreToAdd);
}
return View(model);
}

Related

Why the controls are not getting cleared after i pass empty object to the view in MVC?

suppose i have a form like this
#using (Html.BeginForm("submit", "home", FormMethod.Post))
{
#Html.TextBoxFor(x => x.firstname, new { placeholder = "firstname" })
#Html.TextBoxFor(x => x.lastname, new { placeholder = "lastname" })
<input type="submit" value="submit" />
}
and i have the method as follows
public ActionResult Index()
{
return View(new student());
}
[HttpPost]
public ActionResult submit(student _student) {
return View("index", new student());
}
my question is when i make a post request to the same method ie. index by passing an empty object, the controls are getting cleared but when i make a post request to the submit method the value of the controls is not getting cleared. why is that happening? why do i have to clear it using
ModelState.Clear() but not by passing any empty object?

how to insert the selected value from DDL to DB by MVC Razor

hi I have MVC Razor application as e catalog and I used drop down-list to bind data from DB but the DDl bind the same value from DB as if I have three categories " x , Y , Z" the DDL returned similar values " Z ,Z , Z ".As it have the last value "y" . also I tried to insert the selected value "ID" to DB when user selected the item from DDL but I couldn't and it returned false selected value.
public class CategoryController : Controller
{
private AndriodContext db = new AndriodContext();
List<SelectListItem> items = new List<SelectListItem>();
List<string> category = new List<string>();
SelectListItem s = new SelectListItem();
//
// GET: /Category/
public ActionResult Index()
{
var x = db.Categories.Where(y => y.Active == true).ToList();
return View(x);
}
public ActionResult Create()
{
var data = db.Categories.ToList().Distinct();
List<string> x = new List<string>();
foreach (var t in data)
{
s.Text = t.Name;
s.Value = t.Cat_ID.ToString();
items.Add(s);
}
ViewBag.Parent = items;
return View();
}
//
// POST: /Category/Create
[HttpPost]
public ActionResult Create(Category category, IEnumerable<HttpPostedFileBase> files)
{
var data = db.Categories.ToList().Distinct();
List<SelectListItem> items = new List<SelectListItem>();
foreach (var t in data)
{
SelectListItem s = new SelectListItem();
s.Text = t.Name;
s.Value = t.Cat_ID.ToString();
items.Add(s);
if (s.Selected)
{ category.Parent_ID = int.Parse(s.Value); }
}
db.Categories.Add(category);
db.SaveChanges();
return RedirectToAction("Index");
}
}
#using (Html.BeginForm("Create", "Category", FormMethod.Post, new { enctype = "multipart/form-data", #data_ajax = "false" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="editor-field create-Bt3">
#Html.DropDownList("Parent", new SelectList(ViewBag.Parent, "Value", "Text"), "- Select Parent -")
</div>
<div>
<p class="create-Bt ">
<input type="submit" value="Create" />
</p>
</div>
<br />
<br />
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</fieldset>
}
you need to import jquery 1.7.1.min.js(DOM) in viewpage :
get the jquery DOM from jquery website(http://blog.jquery.com/2011/11/21/jquery-1-7-1-released/).
then in button click (<input type="submit" value="Create" onclick="GetDropDownValue();"/>) :
wrote a javascript function :
<script type="text/javascript" language="javascript">
function GetDropDownValue()
{
$("#hdnParentId").val($("#Parent").val());
}
</script>
The best practice to use a model to bind the dropdownlist instead of ViewBag.
If you don't want to use model the you can do one trick.
you put a hidden field(<input type="hidden" name="hdnParent" id="hdnParentId" />) in view page and calculate selected value of dropdownlis by simple jquery using :
$("#Parent").val();.
make the dropdownlist :
#Html.DropDownList("Parent", new SelectList(ViewBag.Parent, "Value", "Text"), "- Select Parent -",new{ id="Parent" });
After that you get a string parameter in HTTPPOST in controller :
[HttpPost]
public ActionResult Create(string hdnParent) //hdnParent is the name of dropdownlist
{
//now you can get the seleced value from "hdnParent".
//do the stuffs
return View();
}

MVC3 Multiple Models - single page

I have a _layout page which has a login box (partial view) and that view has it's own model. So the controller looks like this:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(LoginModel loginModel)
{
if(ModelState.IsValid)
{
var g = new GallaryImage();
var user = g.LoginUser(loginModel.Username, loginModel.Password);
if(user != null)
{
FormsAuthentication.SetAuthCookie(user.username, false);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "Invalid Username/Password");
}
return View(loginModel);
}
But as soon as my main content page needs a model, my web app fails because the Login box expects a LoginModel type, but my content page is sending a different model:
This is the GET method for my main Index screen:
public ActionResult Index()
{
IndexModel model = new IndexModel();
var g = new GallaryService.GallaryImage();
var i = g.GetRandomImage();
if (i != null)
model.RandomImageUrl = "~/Images/Watermarks/" + i.filename;
return View(model);
}
So, my main content page has an IndexModel, but my partial view has a LoginModel. When I try run it, I get an error:
"The model item passed into the dictionary is of type 'GalleryPresentation.Models.IndexModel', but this dictionary requires a model item of type 'GalleryPresentation.Models.LoginModel'."
How do I handle this - My _layout needs the model for the login box.
As requested, here is the Loginbox cshtml file.
#using GalleryPresentation.Models
#model LoginModel
<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
#using (Html.BeginForm("index", "Account", FormMethod.Post))
{
<table class="smallBox">
<tr>
<td>#Html.LabelFor(m => m.Username)</td>
<td>#Html.TextBoxFor(m => m.Username, new { #class = "smallText" })</td>
<td>#Html.LabelFor(m => m.Password)</td>
<td>#Html.PasswordFor(m => m.Password, new { #class = "smallText" })</td>
</tr>
<tr>
<td colspan="4" align="right"><input type="submit" value="Login"/></td>
</tr>
<tr>
<td colspan="2">#Html.ValidationSummary()</td>
</tr>
</table>
}
And the Index.cshtml file (THe main content screen) has this:
#using GalleryPresentation.Models
#model IndexModel
#{
ViewBag.Title = "Craig and Melanie's Digital Moments";
}
<br/>
<div style="text-align: center">
<img src="#Url.Content( Model.RandomImageUrl)" alt="#ViewBag.Title" />
</div>
Questions like this aren't always the easiest to answer because there isn't a straightforward solution. There are several issue though that should be considered. If it is possible, I would recommend that you handle login validation errors in a separate view. The partial view for the small login box then does not require a strongly-typed view model.
There's no perfect solution, but I don't think that it makes a lot of sense for you to always be creating LoginModel objects on every request that renders a view which depends on _Layout. The solution below advocates the creation of a separate login view which can be used for explicit login attempts and for the handling of any login failures.
If you have any trouble following this, feel free to your question in a comment and I'll do my best to answer.
Login Box
#using (Html.BeginForm("Index", "Account"))
{
<table class="smallBox">
<tr>
<td>Username</td>
<td>#Html.TextBox("Username", new { #class = "smallText" })</td>
<td>Password</td>
<td>#Html.Password("Password", new { #class = "smallText" })</td>
</tr>
<tr>
<td colspan="4" align="right"><input type="submit" value="Login"/></td>
</tr>
</table>
}
Account Controller
public ActionResult Login()
{
return View();
}
public ActionResult RetryLogin()
{
ModelState.AddModelError(null, "The Username or Password you entered is invalid. Please try again.");
return View("Login");
}
[HttpPost]
public ActionResult Index(LoginModel loginModel)
{
if(ModelState.IsValid)
{
var g = new GallaryImage();
var user = g.LoginUser(loginModel.Username, loginModel.Password);
if(user != null)
{
FormsAuthentication.SetAuthCookie(user.username, false);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "Invalid Username/Password");
}
return RedirectToAction("RetryLogin");
}
Login View
#using (Html.BeginForm("Index", "Account"))
{
#Html.ValidationSummary()
<!-- login form here -->
}

Why is my mvc3 viewmodel not updating correctly?

I am writing an image upload form using ASP.NET MVC 3.
In the view, please notice that I am displaying #Model.ImagePath as text and as a #Html.TextBoxFor(model => model.ImagePath).
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data" }))
{
#Html.HiddenFor(model => model.BannerSlideId)
#Html.HiddenFor(model => model.Key)
<p>
Image Path:#(Model.ImagePath)<br />
Image Path in TextBoxFor: #Html.TextBoxFor(model => model.ImagePath)
</p>
<p>
<label class="styled">Upload Slide Image</label>
<input type="file" name="image" />
</p>
<p>
<button type="submit">Save</button> #Html.ActionLink("Back to List", "Index")
</p>
}
I then select an image using the file input, and I submit the form to the Controller.
Controller action:
[HttpPost]
public ActionResult Create(BannerSlide model, HttpPostedFileBase image)
{
if (image != null)
model.ImagePath = image.FileName;
return View("Edit", model);
}
When I debug with a breakpoint, the image.FileName string is assigned to model.ImagePath. However, when I get back to the View I get Two different values from ImagePath.
Results
Image Path:#(Model.ImagePath)<br />
Correctly returns the image filename that was assigned. But,
Image Path in TextBoxFor: #Html.TextBoxFor(model => model.ImagePath)
Incorrectly returns blank!
Any ideas for why this is happening?
You should remove the ImagePath property from ModelState if you intend to modify it in your POST controller action or HTML helpers such as TextBoxFor will first look for a value inside ModelState when binding and then in the model:
if (image != null)
{
ModelState.Remove("ImagePath");
model.ImagePath = image.FileName;
}
The TextBoxFor HtmlHelper method, and all input helper methods for that matter, set the value of the input control to the value held in ModelState rather than immediately binding to the property of the ViewModel. Since the ImagePath present in the POST to your Create method, it is displayed as a blank value on the subsequent Response.
I would suggest to use following attribute on such actions
public class ModelStateFixAttribute : ActionFilterAttribute
{
public ModelStateFixAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
String[] arrKeys = new string[modelState.Keys.Count];
modelState.Keys.CopyTo(arrKeys, 0);
foreach (string key in arrKeys)
{
if (modelState.IsValidField(key))
modelState.Remove(key);
}
}
}

Html Helper to get values from Model State

Asp.Net MVC 3
I seem to have a similar problem as this post answered by Darin Dimitrov. So, Darin if you are reading this, please help :)
asp.net-mvc2 - Strongly typed helpers not using Model?
The problem I have is I am looking for an html helper that will contain the posted value in the modelstate.
For example, if I use an editor for like this:
#Html.EditorFor(model => model.SelectedTags)
I can see the value that was posted. The problem is I need a way to get this value without creating a textbox, I just want the string because I need it in some javascript.
I've tried DisplayFor, but that doesn't contain the posted value:
#Html.DisplayFor(model => model.SelectedTags)
By the way, I don't find this behavior intuitive AT ALL. I spent a few hours debugging ModelStateToTempDataAttribute from MVCContrib thinking it was a bug in their code to Import/Export Model State.
Thanks for any help!
Edit - Added Repro Code
Take these steps to reproduce:
Start project. Property1 should be blank (required), Property2 should have "abc"
Change Property2 to "xxx"
Submit Form (notice ClientValidationEnabled is False)
Form is posted, redirect, load (PRG). Property2 textbox has"xxx" and right below you will see "abc" from DisplayFor.
Controller
[ModelStateToTempData] //From MVCContrib
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
//simulate load from db
var model = new FormModel() { MyProperty2 = "abc" };
return View(model);
}
[HttpGet]
public ActionResult Success()
{
return View();
}
[HttpPost]
public ActionResult Index(FormModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Success");
}
else
{
return RedirectToAction("Index");
}
}
}
Model
public class FormModel
{
[Required]
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
}
View
#model MvcApplication4.Models.FormModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>FormModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.MyProperty1)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyProperty1)
#Html.ValidationMessageFor(model => model.MyProperty1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MyProperty2)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyProperty2)
#Html.ValidationMessageFor(model => model.MyProperty2)
</div>
#Html.DisplayFor(model => model.MyProperty2)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Config:
<add key="ClientValidationEnabled" value="false" />
ModelStateToTempData (MVCContrib):
public class ModelStateToTempDataAttribute : ActionFilterAttribute
{
public const string TempDataKey = "__MvcContrib_ValidationFailures__";
/// <summary>
/// When a RedirectToRouteResult is returned from an action, anything in the ViewData.ModelState dictionary will be copied into TempData.
/// When a ViewResultBase is returned from an action, any ModelState entries that were previously copied to TempData will be copied back to the ModelState dictionary.
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
var controller = filterContext.Controller;
if(filterContext.Result is ViewResultBase)
{
//If there are failures in tempdata, copy them to the modelstate
CopyTempDataToModelState(controller.ViewData.ModelState, controller.TempData);
return;
}
//If we're redirecting and there are errors, put them in tempdata instead (so they can later be copied back to modelstate)
if((filterContext.Result is RedirectToRouteResult || filterContext.Result is RedirectResult) && !modelState.IsValid)
{
CopyModelStateToTempData(controller.ViewData.ModelState, controller.TempData);
}
}
private void CopyTempDataToModelState(ModelStateDictionary modelState, TempDataDictionary tempData)
{
if(!tempData.ContainsKey(TempDataKey)) return;
var fromTempData = tempData[TempDataKey] as ModelStateDictionary;
if(fromTempData == null) return;
foreach(var pair in fromTempData)
{
if (modelState.ContainsKey(pair.Key))
{
modelState[pair.Key].Value = pair.Value.Value;
foreach(var error in pair.Value.Errors)
{
modelState[pair.Key].Errors.Add(error);
}
}
else
{
modelState.Add(pair.Key, pair.Value);
}
}
}
private static void CopyModelStateToTempData(ModelStateDictionary modelState, TempDataDictionary tempData)
{
tempData[TempDataKey] = modelState;
}
}
you can read these values from modelstate dictionary like
<%:Html.ViewData.ModelState["key"] %>
However, it seems to me that SelectedTags is an enumeration of objects that is displayed for editing when you call EditorFor(model=>model.SelectedTags). in this scenario it is highly unlikely that you get anything by calling Html.ViewData.ModelState["SelectedTags"]. you will have to iterate over the keys in ModelState dictionary instead and check if key begins with SelectedTags prefix and then u can read its value accordinly.
In your Views -> Shared -> DisplayTemplates, create SelectedTags.cshtml
This will be your display template. Inside write something on the lines of
#model YourProject.WebUI.Models.SelectedTags
#for(int i = 0; i < Model.Tags.Count(); i++){
// Assuming that selected tags contains a list of tags.
// Replace <p> with whatever feels suitable
<p>Model.Tags[i]</p>
}
You can then use this display template in your views:
#Html.DisplayFor(model => model.SelectedTags,"SelectedTags")
This should also work:
#Html.DisplayFor(model => model.SelectedTags)

Resources