Keep ASP MVC FormCollection values after posting back from controller - c#

On my Index view, I need to allow the user to enter search criteria. This criteria is sent back to the Index controller in the form of a FormCollection object. I then extract the search criteria, pull the requested information from the database, and send the user back to the Index view. However, once the user get's back to the index view with the requested info, the data from the FormCollection object is now blank.
I would like to be able to keep the user's search criteria in the three text boxes that I use, however I'm not sure how using a FormCollection. Does anyone know either how to do this or another I should use?
View
#using(Html.BeginForm())
{
<div id="searchBox" class="boxMe">
<div id="zipBox" style="float: left; padding-left: 20px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px; vertical-align: top;">
#Html.Raw("Zip Code")
#Html.TextArea("ZipSearch", new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
</div>
<div id="dateBox" style="float: left; padding-right: 10px; padding-top: 20px;">
#Html.Raw("Effective on this date")
#Html.TextBox("DateSearch", null, new { style="width: 80px;"})
</div>
<div id="stateBox" style="float: left; padding-right: 20px; padding-top: 20px;">
#Html.Raw("State")
#Html.TextBox("StateSearch", null, new { style = "width: 25px;" })
<button type="submit">Search</button>
</div>
</div>
<div style="clear: both;"></div>
}
Controller
public ViewResult Index(FormCollection searchDetails = null)
{
string zip = searchDetails["ZipSearch"];
string date = searchDetails["DateSearch"];
string state = searchDetails["StateSearch"];
if (String.IsNullOrWhiteSpace(zip) && String.IsNullOrWhiteSpace(date) && String.IsNullOrWhiteSpace(state))
{
return View();
}
string[] zipArray;
DateTime effectiveDate;
//Convert date string to DateTime type
if (String.IsNullOrWhiteSpace(date))
{
effectiveDate = DateTime.MinValue;
}
else
{
effectiveDate = Convert.ToDateTime(date);
}
//Conduct search based on Zip Codes
if (!String.IsNullOrWhiteSpace(zip))
{
//Create array and remove white spaces
zipArray = zip.Split(',').Distinct().ToArray();
for (int i = 0; i < zipArray.Length; i++)
{
zipArray[i] = zipArray[i].Trim();
}
//Add zip codes to list object then send back to view
List<ZipCodeTerritory> zips = new List<ZipCodeTerritory>();
if (String.IsNullOrWhiteSpace(state))
{
foreach (var items in zipArray)
{
var item = from z in db.ZipCodeTerritory
where z.ZipCode.Equals(items) &&
z.EffectiveDate >= effectiveDate
select z;
zips.AddRange(item);
}
}
else
{
foreach (var items in zipArray)
{
var item = from z in db.ZipCodeTerritory
where z.ZipCode.Equals(items) &&
z.EffectiveDate >= effectiveDate
select z;
zips.AddRange(item);
}
}
return View(zips);
}
//Zip code was not specified so search by state
if (!String.IsNullOrWhiteSpace(state))
{
var items = from z in db.ZipCodeTerritory
where z.StateCode.Equals(state) &&
z.EffectiveDate >= effectiveDate
select z;
return View(items);
}
//Neither zip code or state specified, simply search by date
var dateOnly = from z in db.ZipCodeTerritory
where z.EffectiveDate >= effectiveDate
select z;
return View(dateOnly);
}
EDIT
Following the instructions below I've created my View model like so:
public class ZipCodeIndex
{
public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
public string searchZip { get; set; }
public string searchDate { get; set; }
public string searchState { get; set; }
}
However in my View I cannot access any of these properties. The header for the view is written like this:
#model IEnumerable<Monet.ViewModel.ZipCodeIndex>
However all the TextBoxFor and the TextAreaFor helpers say none of the specified properties exist.
#using(Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post))
{
<div id="searchBox" class="boxMe">
<div id="zipBox" style="float: left; padding-left: 20px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px; vertical-align: top;">
#Html.Raw("Zip Code")
#Html.TextAreaFor(model => model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
</div>
<div id="dateBox" style="float: left; padding-right: 10px; padding-top: 20px;">
#Html.Raw("Effective on this date")
#Html.TextBoxFor(model => model.searchDate, null, new { style="width: 80px;"})
</div>
<div id="stateBox" style="float: left; padding-right: 20px; padding-top: 20px;">
#Html.Raw("State")
#Html.TextBoxFor(model => model.searchState, null, new { style = "width: 25px;" })
<button type="submit">Search</button>
</div>
</div>
<div style="clear: both;"></div>
}
FINAL EDIT
Missed that the page was looking for an IEnuerable Model object. Changed the header to this and fixed the problem.
#model Monet.ViewModel.ZipCodeIndex

Ok so the first thing you want to do is create a model just a new class file that looks something like this.
public class AddressModel
{
public int zip{ get; set; }
public string state{ get; set; }
public string date{ get; set; }
}
Then another new class file called a view model something like this. This way you can reference it for different things. Have like your search address then return the results in seperate list.
public class AddressViewModel
{
public AddressModel SearchAddress { get; set; }
public List<AddressModel> ResultsAddress{ get; set; }
}
Then in your view you reference the viewmodel like this #model MVCTestProject.ViewModels.InsertPage
on the top line.
Then these will be your text boxes.
#using (Html.BeginForm("Submit", "Insert", FormMethod.Post))
{
#Html.TextBoxFor(model => model.SearchAddress.zip)
#Html.TextBoxFor(model => model.SearchAddress.state)
#Html.TextBoxFor(model => model.SearchAddress.date)
<input id="button" name="button" type="submit" value="submit" />
}
And in your controller it will be similar to this.
public ActionResult Submit(AddressViewModel model)
{
model.ResultsAddress = (from z in db.ZipCodeTerritory
where z.StateCode.Equals(state) &&
z.EffectiveDate >= model.SearchAddress.date
select new AddressModel {
date = z.effectiveDate }).toList();
return View("viewName", model);
}
This will return your original Search criteria and the results. Is probably not all a hundred percent functional but the main ideas are there, and I can help you through problems if you decide to go down this path.
to display the results
#for (int i = 0; i < Model.ResultsAddress.Count; i++)
{
#Html.DisplayFor(model => model.ResultsAddress[i].date)
}
or for just displaying them top one can be used for editing and resubmitting the data.
#foreach (var item in Model.ResultsAddress)
{
<div>#item.date</div>
}

Ryan Schlueter is right that you should use ASP.NET MVC principles such as model, autobinding to model fields in action parameters and so on. But the main in this case you should use strongly typed view:
Action:
public ActionResult SomeAction(ModelClass model)
{
....
return("ViewName", model)
}
View:
#model SomeNamespace.ModelClass
#using (Html.BeginForm("Submit", "Insert", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Property1)
#Html.TextBoxFor(model => model.Property2)
#Html.TextBoxFor(model => model.Property3)
<input id="button" name="button" type="submit" value="submit" />
}

Related

Changing the model field with TextBoxFor and passing the model to the controller

I have a model with property of the viewmodel:
public class FilterModel
{
[DisplayName("Start")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{MM/dd/yyyy HH:mm}")]
public DateTime StartDate { get; set; }
[DisplayName("End")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{MM/dd/yyyy HH:mm}")]
public DateTime EndDate { get; set; }
public bool ShowActive { get; set; }
}
My view update StartDate and EndDate on the model using TextBoxFor:
#using (Html.BeginForm("ApplyFilter", "Shipment", FormMethod.Post))
{
<div class="input-group">
<button type="submit" class="btn btn-default btn-sm" style="margin: 3px">
<i class="fa fa-check"></i>
</button>
#Html.TextBoxFor(m => m.Filter.StartDate, new { #minlength = "4", #maxlength = "20", #style = "margin: 3px; border: solid 1px #d5d5d5;" })
#Html.TextBoxFor(m => m.Filter.EndDate, new { #minlength = "4", #maxlength = "20", #style = "margin: 3px; border: solid 1px #d5d5d5;" })
</div>
}
The method in controller ApplyFilter:
public ActionResult ApplyFilter(FilterModel filter)
{
var vm = Load(filter);
return View("Index", vm);
}
The properties StartDate and EndDateare correct updated but property ShowActive is always setted with False. The property ShowActive does not pass its current value in the view to the controller when ApplyFilter method is called.
you have to add ShowActive to your view
#using (Html.BeginForm("ApplyFilter", "Shipment", FormMethod.Post))
{
#Html.HiddenFor(m => m.ShowActive)
....
}
You missed ShowActive on your View:
You can add:
<div class="checkbox">
#Html.CheckBoxFor(x => x.ShowActive, new { htmlAttributes = new { #type = "checkbox", #id = "checkbox-showactive" } })
<label for="checkbox-showactive"> Active</label>
</div>
If you want to handle that property hidden to user:
#model FilterModel
---
#Html.HiddenFor(m => Model.ShowActive)

Page not redirecting as intended

I am trying to validate input data and if the validation is true, go to the next view. However, I can see my code is not existing the present page.
Controller:
public ActionResult ForgotLoginId()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotLoginId(Testing ForgotLIDAuthentication)
{
if (ModelState.IsValid)
{
using(SUPRTestingDBEntities2 db = new SUPRTestingDBEntities2())
{
if (obj != null)
{
Session["LoginID"] = obj.EmailID.ToString();
Session["EmailID"] = obj.TaxID.ToString();
return RedirectToAction("LIDAuthentication");
}
else if (obj == null)
{
return View();
}
}
}
return View();
}
public ActionResult LIDAuthentication()
{
if (Testing.IsUserValid())
{
return View();
}
else
{
return RedirectToAction("ForgotLoginID");
}
}
View:
#using (Html.BeginForm("ForgotLoginID", "Corporation", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
if (ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
<div>
<textarea name="EmailId" style="width:50%; border-color: black" required>#Html.TextAreaFor(a => a.EmailID)</textarea>
<text>#Html.ValidationMessageFor(a => a.EmailID)</text>
<textarea name="TaxID" style="width:30%; border-color: black" required placeholder="xxxxxxxxx" maxlength="9">#Html.TextAreaFor(a => a.Password)</textarea>
<text>#Html.ValidationMessageFor(a => a.Password)</text>
<button type="submit" style="width: 20%; color: white; background-color: deepskyblue; border-color:black" value="SubmitRequest" name="Submitbtn"><b>Submit</b></button>
</div>
}
Model:
public partial class Testing
{
public int LoginID { get; set; }
[Required]
public string EmailID { get; set; }
[Required]
public int TaxID { get; set; }
public string Password { get; set; }
[Required]
public string CorporationName { get; set; }
public static bool IsUserValid()
{
HttpContext context = HttpContext.Current;
if (context.Session["LoginID"] != null)
{
return true;
}
else
return false;
}
}
Here, the page not exiting the current view. When I tried to insert breakpoint after [httppost] in Controller, it is not even hitting the code.
Also, I am not sure why my view has something in fields, I don't know what it is. Not sure if this is affecting my output.
ForgotLoginID View
Please help me with this.
I'm not sure if this will fix your problem, but your view code should look something like:
#using (Html.BeginForm("ForgotLoginID", "Corporation", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
if (ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
#Html.TextAreaFor(a => a.EmailID)
#Html.ValidationMessageFor(a => a.EmailID)
#Html.TextAreaFor(a => a.Password)
#Html.ValidationMessageFor(a => a.Password)
<button type="submit" style="width: 20%; color: white; background-color: deepskyblue; border-color:black" value="SubmitRequest" name="Submitbtn"><b>Submit</b></button>
}

.NET MVC - Map text from TextBoxFor to an Array

My model is set up so the controller can deliver a Json response of data from user input. The 'rgba' property of my model is an array of ints. If a user enters text of say '255, 0, 0, 1' into the TextBoxFor for 'rgba', the text is not mapping into an array (which I thought was supposed to happen automagically). Instead, int[0] is what makes it to the controller.
I've tried all the potential solutions I could find on here, including passing a FormCollection object to controller. I've tried to get the TextBoxFor value using JS/jQuery and manipulate the data, but can't figure out how to pass the manipulated data back to the model (this seems less than ideal, like there should be an easy way to do this in .Net).
Controller:
public class HomeController : Controller
{
public IActionResult NewColor()
{
Rootobject newColor = new Rootobject();
return View(newColor);
}
[HttpPost]
public IActionResult NewColor(Rootobject color)
{
var json = JsonConvert.SerializeObject(color);
return Json(json);
}
}
Model:
public class Rootobject
{
public Color[] colors { get; set; }
}
public class Color
{
public string color { get; set; }
public string category { get; set; }
public string type { get; set; }
public Code code { get; set; }
}
public class Code
{
public int[] rgba { get; set; }
public string hex { get; set; }
}
View:
#model WebAppPlayground.Models.Rootobject
#{
ViewData["Title"] = "New Color";
}
<style>
input:focus {
border-radius: 5px;
}
input {
padding: 2px;
border-radius: 5px;
border-style: ridge;
}
</style>
<h2>New Color</h2>
<h4>Color</h4>
<hr />
<center>
#using (Html.BeginForm("NewColor", "Home", FormMethod.Post, new { id = "form" }))
{
<table style="border-collapse:separate; border-spacing: 5px 5px">
<tbody>
<tr class="form-group" for="Color">
<td>Color</td>
<td>#Html.TextBoxFor(m => m.colors[0].color)</td>
</tr>
<tr class="form-group">
<td class="">Category</td>
<td>#Html.TextBoxFor(m => m.colors[0].category)</td>
</tr>
<tr class="form-group">
<td class="">Type</td>
<td>#Html.TextBoxFor(m => m.colors[0].type)</td>
</tr>
<tr class="form-group">
<td class="">rgba</td>
<td>#Html.TextBoxFor(m => m.colors[0].code.rgba, new { id = "rgba"})</td>
</tr>
<tr class="form-group">
<td class="">Hex</td>
<td>#Html.TextBoxFor(m => m.colors[0].code.hex)</td>
</tr>
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
}
<div>
<a asp-action="Index">Back to List</a>
</div>
</center>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I would like my Controller to receive an array of Int's via rgba TextBoxFor where user enters text eg 255, 0, 0, 1.
I think there is something (obvious) I am overlooking or not understanding.
****Updated controller post method to remove added 'rgba_str' prop per #i_ll_be_back 's answer and deliver desired Json data:
[HttpPost]
public IActionResult NewColor(Rootobject color)
{
var json = JsonConvert.SerializeObject(color);
JObject jsonObject = JObject.Parse(json);
JObject code = (JObject)jsonObject["colors"][0]["code"];
code.Property("rgba_str").Remove();
return Json(jsonObject);
}
Try the following reshaping the Code class in the following:
public class Code
{
public int[] rgba { get; set; }
public string rgba_str
{
get
{
var res = "";
for(int i = 0; i < rgba.Length; i++) {
res += rgba[i] + (i != rgba.Length - 1 ? "," : "");
}
return res;
}
set
{
var strArray = value.Split(',');
rgba = new int[strArray.Length];
for(int i = 0; i < strArray.Length; i++) {
rgba[i] = int.Parse(strArray[i]);
}
}
}
public string hex { get; set; }
}
Now bind the TextBoxFor with the rgba_str field

Pass data from Model to View

I have a model with a list of data and my View accesses the model properties and renders the database status. There are several boxes in my view which displays color according to their respective values in the database. And each div corresponds to one item in the database.
I know I haven't coded my View in a correct way but it is what I want my view to look like (each div ID corresponding to the respective boxID in the database and displaying its respective color). And I want 500 database values represented in the View.
#model List<My.Namespace.BoxStatusModel>
<table>
<tr class="first">
<td class="dclass1">
<div id="#boxStatus.boxID" class="top #boxStatus.Color">
<div id="#boxStatus.boxID" class="top #boxStatus.Color">
<div id="#boxStatus.boxID" class="bottom #boxStatus.Color">
<div id="#boxStatus.boxID" class="bottom #boxStatus.Color">
</td>
<td class="dclass2">
<div id="#boxStatus.boxID" class="top #boxStatus.Color">
<div id="#boxStatus.boxID" class="top #boxStatus.Color">
<div id="#boxStatus.boxID" class="bottom #boxStatus.Color">
<div id="#boxStatus.boxID" class="bottom #boxStatus.Color">
</td>
...
</tr>
<tr class="second">
...
</tr>
</table>
My Controller
public class HomeController : Controller
{
public ActionResult Index()
{
MySQL msql = new MySQL();
List<List<string>> results = msql.SelectTable("Select boxID, boxStatus from BoxStatusModel");
List<BoxStatusModel> model = new List<BoxStatusModel>();
for (int i = 0; i < results.Count; i++)
{
model.Add(new BoxStatusModel()
{
slotID = results[i][0],
currentStatus = results[i][1]
});
}
return View(model);
}
}
My ViewModel
namespace My.ViewModels
{
public class BoxStatusModel
{
public int ID { get; set; }
public string boxID { get; set; }
public string boxStatus { get; set; }
public string Color
{
get
{
switch (boxStatus)
{
case "busy":
return "red";
case "available":
return "green";
default:
return "";
}
}
}
}
}

Returning List<Object> in HTML.BeginForm MVC 5

Hey Guys I need help on this, I know I can return List<string> in HTML.BeginForm which looks like this:
#using (Html.BeginForm("Test", "Home", FormMethod.Post, new {#class = "form-horizontal", role = "form"}))
{
<textarea name="logic" style="width: 10em; height: 10em;"></textarea>
<textarea name="logic" style="width: 10em; height: 10em;"></textarea>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
I can return List<string> Logic on my Controller HTTPOST
But my question is how will I do it if I need to return a List of Object? Not using a Model because I need to return 2 Objects so I am was thinking like my controller should get it something like this
public ActionResult Test(List<Model1> model1, List<Model2> model2)
Thanks for those who can help
You can do in provided way
First declare class
public class TestModel
{
public string Name { get; set; }
public string Text { get; set; }
}
Second Controller to accept list of model
[HttpPost]
public ActionResult Test(List<TestModel> model1, List<TestModel> model2)
{
return View();
}
Last, View to pass model values
#using (Html.BeginForm("Test", "Home", FormMethod.Post, new {#class = "form-horizontal", role = "form"}))
{
<textarea name="model1[0].Name" style="width: 10em; height: 10em;"></textarea>
<textarea name="model1[0].Text" style="width: 10em; height: 10em;"></textarea>
<textarea name="model1[1].Name" style="width: 10em; height: 10em;"></textarea>
<textarea name="model1[1].Text" style="width: 10em; height: 10em;"></textarea>
<textarea name="model2[0].Name" style="width: 10em; height: 10em;"></textarea>
<textarea name="model2[0].Text" style="width: 10em; height: 10em;"></textarea>
<textarea name="model2[1].Name" style="width: 10em; height: 10em;"></textarea>
<textarea name="model2[1].Text" style="width: 10em; height: 10em;"></textarea>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
Put the breakpoint inside Test controller you will see list populated
I would suggest to wrap these lists into a view model class like this -
public class ModelView
{
public List<Model1> model1 { get; set; }
public List<Model2> model2 { get; set; }
}
...and then the controller will look like -
public ActionResult Test(ModelView modelView)
Within the controller you can easily access the lists.
modelView.model1
modelView.model2
If you want to generate a view with multiple model objects then you need to create a ViewModel comprising properties that are needed from those models. And then reference the view with this ViewModel.
public class Model1
{
public string prop11 { get; set; }
public string prop12 { get; set; }
}
public class Model2
{
public string prop21 { get; set; }
public string prop22 { get; set; }
}
public class ViewModel
{
public List<Model1> model1 { get; set; }
public List<Model2> model2 { get; set; }
}
Then generate the view referencing the viewmodel that will get the properties from both models.
controller action that will be hit from that view:
public ActionResult Test(ModelView modelView) // you can access the viewmodel properties

Categories

Resources