MVC3 Model Binding - List to Hidden fields - c#

I have a peculiar problem - I have a ViewModel with a List in it used to display a list of images:
public List<int> TrackerKeys { get; set; }
This is used in two places in the page:
#for (int i = 0; i < Model.TrackerKeys.Count(); i++)
{
#Html.GenerateImage(PageModes.Http, Model.TrackerKeys[i])
}
And also
#for (int i = 0; i < Model.TrackerKeys.Count(); i++)
{
#Html.HiddenFor(model => model.TrackerKeys[i])
}
This is sat inside a form - when the form is submitted, if a validation error occurs, the TrackerKeys property is updated with a new random set of numbers. I am passing the previous list back to ensure the user does not see the same image again.
Tracker keys is correctly set and passed back to the View.
My problem is:
The images correctly display the new images based on the new values in the list
However
The values of the hidden fields are not updated to the new values - they retain the original values.
Any ideas? Is this a bug with MVC3? any input would be greatly appreciated. Thank you.
Edit
Html before submission:
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/26.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/33.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/8.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/30.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/6.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/18.gif" width="35" />
And
<input id="TrackerKeys_0_" name="TrackerKeys[0]" type="hidden" value="26" />
<input id="TrackerKeys_1_" name="TrackerKeys[1]" type="hidden" value="33" />
<input id="TrackerKeys_2_" name="TrackerKeys[2]" type="hidden" value="8" />
<input id="TrackerKeys_3_" name="TrackerKeys[3]" type="hidden" value="30" />
<input id="TrackerKeys_4_" name="TrackerKeys[4]" type="hidden" value="6" />
<input id="TrackerKeys_5_" name="TrackerKeys[5]" type="hidden" value="18" />
And after the post:
correct new values here...
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/16.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/20.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/11.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/19.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/26.gif" width="35" />
<img alt="Security Character" height="35" src="http://localhost:51414/content/imagescss/15.gif" width="35" />
and...
still retaining old values...
<input id="TrackerKeys_0_" name="TrackerKeys[0]" type="hidden" value="26" />
<input id="TrackerKeys_1_" name="TrackerKeys[1]" type="hidden" value="33" />
<input id="TrackerKeys_2_" name="TrackerKeys[2]" type="hidden" value="8" />
<input id="TrackerKeys_3_" name="TrackerKeys[3]" type="hidden" value="30" />
<input id="TrackerKeys_4_" name="TrackerKeys[4]" type="hidden" value="6" />
<input id="TrackerKeys_5_" name="TrackerKeys[5]" type="hidden" value="18" />
controller action
[HttpPost]
public ActionResult EmployerRegistration(EmployerViewModel Model)
{
if (ModelState.IsValid)
{
//do bits here
}
Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys);
return View(Model);
}

Is this a bug with MVC3?
Oh no, that's by design and it is how all HTML helpers work in ASP.NET MVC. HTML helpers (such as Html.TextBoxFor, Html.HiddenFor, ...) first look at the ModelState when binding their values and after that they look at the Model. So if you intend to modify some property of your model inside the POST controller action and this property was part of the POST body, you will have to remove the old values from the ModelState first:
[HttpPost]
public ActionResult EmployerRegistration(EmployerViewModel Model)
{
if (ModelState.IsValid)
{
//do bits here
}
// we clear all values from the modelstate => this will ensure that
// the helpers will use the values from the model and not those that
// were part of the initial POST.
ModelState.Clear();
Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys);
return View(Model);
}
or if you don't want to clear the entire ModelState (as this will remove all values and any corresponding modelstate errors that might be associated to them) you could remove only those keys that you actually modify:
[HttpPost]
public ActionResult EmployerRegistration(EmployerViewModel Model)
{
if (ModelState.IsValid)
{
//do bits here
}
for (var i = 0; i < Model.TrackerKeys.Count; i++)
{
ModelState.Remove(string.Format("TrackerKeys[{0}]", i));
}
Model.TrackerKeys = Helper.GenerateNewNumbers(Model.TrackerKeys);
return View(Model);
}

Related

ASP.NET MVC app shuts down when i try uploading files

I have a form in my asp.net web app, which sends files uploaded by the user
<form method="post" asp-action="#((Model != null) ? "Update" : "Upload")" asp-controller="Materials" enctype="multipart/form-data">
<input id="cat" type="hidden" name="cat" />
<input id="subcat" type="hidden" name="subcat" />
<input type="file" name="file" max="1" title="#((Model != null) ? "Обновить" : "Загрузить")"/><br />
<input type="file" name="images" multiple title="Загрузить картинки"/><br />
<input id="name" type="text" name="name" maxlength="30" /><br />
<textarea id="desc" name="description" maxlength="199" ></textarea><br/>
<input type="submit" value="Сохранить"/>
</form>
There is one input for a file, and one input for image files, but as soon as I input any files the app just.... shuts down
Here is the method from the controller:
[HttpPost]
public IActionResult Upload(IFormFile file, IFormFileCollection images, string name, string description, string cat, string subcat)
{
FileUploadManager f = new FileUploadManager(environment.WebRootPath + '/');
f.Upload(file,images, name, description, cat + "-" + subcat);
return Redirect(Url.Action("Index", "Material"));
}
the browser does not shut down, the app itself in the VS does, and without any exceptions, it just stops debugger as soon as files contact with HTML
also, I am using Yandex browser

MVC 6 .Net Core1 Create Custom tag helper for Likert Scale

I am trying to create a tag helper for MVC 6 using .Net Core RC1. I have found a few good sources but not something super close, this was the closest I found and took elements I thought I needed to create my existing code:
To start with this is my target HTML:
<span class="form-control">
Highly Disagree
<input type="radio" name="MakingSense" value=1 />
<input type="radio" name="MakingSense" value=2 />
<input type="radio" name="MakingSense" value=3 />
<input type="radio" name="MakingSense" value=4 />
<input type="radio" name="MakingSense" value=5 />
Highly Agree
</span>
Right now I am just trying to get one of the input tags to show. If I can figure that out I will add a loop to get the others. Here is my TagHelper
[HtmlTargetElement("input", Attributes = LikertForAttributeName)]
public class LikertTagHelper : TagHelper
{
private const string LikertForAttributeName = "likert-for";
[HtmlAttributeName(LikertForAttributeName)]
public string ModelField { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var content = new StringBuilder();
var input = new TagBuilder("input");
input.MergeAttribute("type", "radio");
input.MergeAttribute("name", ModelField);
input.MergeAttribute("value", "1");
content.AppendLine(input.ToString());
output.Content.SetContent(content.ToString());
output.PreContent.SetHtmlContent("<span class=\"form-control\"");
output.PostContent.SetHtmlContent("</span>");
}
}
Here is my razor cshtml:
<input likert-for="CharacterUnderstanding" />
However, I only get this as my html output:
<input />
So it is picking up the tag and processing it but not as I expect. Help in where I went wrong will be much appreciated.
Your tag helper starting point is an input tag, so content starts being a self closing input tag.
The first thing you want to do is turn that into a span tag, for example:
//Replace initial output (an input tag) with a span
var outerTag = new TagBuilder("span");
outerTag.AddCssClass("form-control");
output.MergeAttributes(outerTag);
output.TagName = outerTag.TagName;
output.TagMode = TagMode.StartTagAndEndTag;
Now since output is a span tag you can start adding the inner contents:
The Highly Agree, Highly Disagree labels can be added as pre/post content (still inside the span):
//Add Pre/Post texts
output.PreContent.SetHtmlContent("Highly Disagree");
output.PostContent.SetHtmlContent("Highly Agree");
The radio buttons can be appended to the content of the span:
//Add the radio buttons
for(var x =0; x<5; x++)
{
var input = new TagBuilder("input");
input.MergeAttribute("type", "radio");
input.MergeAttribute("name", ModelField);
input.MergeAttribute("value", x.ToString());
output.Content.Append(input);
}
With this tag helper in place, the following line in the razor view:
<input likert-for="CharacterUnderstanding" />
Is rendered as:
<span class="form-control">
Highly Disagree
<input name="CharacterUnderstanding" type="radio" value="0">
<input name="CharacterUnderstanding" type="radio" value="1">
<input name="CharacterUnderstanding" type="radio" value="2">
<input name="CharacterUnderstanding" type="radio" value="3">
<input name="CharacterUnderstanding" type="radio" value="4">
Highly Agree
</span>
As a side note, you want to be careful when adding contents using the overloads that accept strings. In your original code the line content.AppendLine(input.ToString()); was actually appending Microsoft.AspNet.Mvc.Rendering.TagBuilder instead of the tag builder content.

ASP.MVC5 - Foreach with property array

Well! I'm using ASP.MVC5. What I'm trying to do is to loop an array that contains objects and each object contains array properties:
I have this class:
public class MylistModels
{
public string Subtitle { get; set; }
public string[] Question { get; set; }
}
The logic is:
I have a POST form in HTML (I can add more fields if I want):
<input type="text" placeholder="Subtitle here" name="lists[0].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[0].Question" />
<input type="text" placeholder="Subtitle here" name="lists[0].Question" />
<input type="text" placeholder="Subtitle here" name="lists[1].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[1].Question" />
<input type="text" placeholder="Subtitle here" name="lists[1].Question" />
<input type="text" placeholder="Subtitle here" name="lists[2].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[2].Question" />
<input type="text" placeholder="Subtitle here" name="lists[2].Question" />
When I click in save I send it to an action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(List<MylistModels> lists)
{
if (ModelState.IsValid)
{
//how to loop the arrays that contains a subtitle with questions array?
return RedirectToAction("Index");
}
return View();
}
When I receive it in the action, it looks like this:
I receive an array of objects and each one has your subtitle related to your questions
Obs.: the "subtitle" is always one in each object and "questions" I can have one or more.
PROBLEM:
How can I loop inside each object array and loop again in each question of this object?
I'm trying to do like this but it's not working:
SubtitleChecklist subtitleCheckList = new SubtitleChecklist();
QuestionChecklist questionChecklist = new QuestionChecklist();
foreach (var list in lists)
{
subtitleCheckList.IdChecklist = idChecklist;
subtitleCheckList.Subtitle = list.Subtitle;
db.subtitleCheckList.Add(subtitleCheckList);
db.SaveChanges();
int idSubtitleChecklist = subtitleCheckList.Id;
for (int i = 0; i < list.Question.Length; i++)
{
questionChecklist.Question = list.Question[i];
questionChecklist.IdSubtitle = idSubtitleChecklist;
db.QuestionChecklist.Add(questionChecklist);
db.SaveChanges();
}
}
Where are you instantiating questionChecklist and subtitleCheckList?
It looks like you are modifying the same objects over and over in your loops.
UPDATE 1:
Move: SubtitleChecklist subtitleCheckList = new SubtitleChecklist();
inside the outer loop and the QuestionChecklist questionChecklist = new QuestionChecklist(); into the inner loop.
Also, what result are you getting now?
You loop through lists object but you assign values and do all other stuff to one and the some object that you defined outside the loop and has nothing to do with your loop.

How to Pass Model from view to Controller

I'm having following view page,
#using (Html.BeginForm())
{
<fieldset class="fs">
#foreach (var item in Model.lstTravelReadyEntities)
{
<label class="Detail1"><b>Associate Id : </b>#item.Var_AssoId </label>
<label class="Detail1"><b>Vertical :</b>#item.Var_Vertical</label>
<label class="Detail1"><b>Visa ValidFrom :</b>#item.Dt_VisaValidFrom </label><br /><br />
<label class="Detail2"><b>Associate Name :</b>#item.Var_AssociateName</label>
<label class="Detail2"><b>Account Name :</b>#item.Var_AccountName</label>
<label class="Detail2"><b>Visa ValidDate :</b>#item.Dt_VisaValidTill</label><br /><br />
<label class="Detail3"><b>Grade HR :</b>#item.Var_Grade</label>
<label class="Detail3"><b>Project Name :</b>#item.Var_Project_Desc</label><br />
}
<h2> Response Details</h2><br />
Supervisor Response :<input type="radio" class="radi"
name="radio" value="yes" onclick="javascript:Getfunc(this.value);">Yes
<input type="radio"
name="radio" value="no"
onclick="javascript:Getfunc(this.value)">No
<div id="es"></div>
<input type="submit" id="insert" value="Submit"
name="Submit" onclick="javascript:InsertDetails(item);"/>
</fieldset>
}
I want pass all the values of this view page to the controller as parameters for inserting these values into the new table.How can i Achieve this?
Use #Html helpers for your controls.
Have a look at this blog entry from Scott Gu. It's about MVC2 but still applies to MVC4.
For a more concrete example, have a look at this question regarding #Html.RadioButtonFor().
Also, I would recommend hooking your events using jquery instead of inline onclick= html attributes.
<script type="text/javascript">
$("form radio").click(function(){ // or whatever selector you need
Getfunc($(this)[0].value);
});
</script>
Finaly, you will need to make sure your #Html.BeginForm posts to an [HttpPost]-decorated action on your controller that takes your Model as parameter.
What is the Problem in Existing code ?
There is no Input Type Text Control in the form and that's the reason information is not being sent to server. TextBox like controls forwards the data for sending the information to Controller Action Method.
Corrective Action
Let's say TextBox is not Required in you case. Then, you can place Hidden Fields for those View Model Properties which are required to be sent to Controller Post Action method.
Example
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post))
{
<fieldset class="fs">
#foreach (var item in Model.lstTravelReadyEntities)
{
<label class="Detail1">
<b>Associate Id : </b>#item.Var_AssoId
</label>
#Html.HiddenFor(i=> item.Var_AssoId) //Added Hidden Field to send this to server
<label class="Detail1">
<b>Vertical :</b>#item.Var_Vertical</label>
#Html.HiddenFor(i => item.Var_Vertical) //When post this Hidden Field will send
<label class="Detail1">
<b>Visa ValidFrom :</b>#item.Dt_VisaValidFrom
</label>
#Html.HiddenFor(i => item.Dt_VisaValidFrom)
<br />
}
<h2>
Response Details</h2>
<br />
</fieldset>
}
For explaining point of view, I excluded some of the controls. Now, You can add Hidden Fields for those Properties which are required to be sent to Action Method.
Also you should use View Models instead of Passing Individual parameter to Action Method.
Hope this will help you.
Hi try like this,
View
#using (Html.BeginForm("SaveAudit", "Controller", FormMethod.Post)
{
}
Controller
[HttpPost]
public ActionResult SaveAudit(AuditModel model, FormCollection collection)
{
}

Knowing the values of check box in the controller with razor

I have a asp.net mvc application with razor engine.
In a view Home i have this snippet:
<section id="form_admin">
<form action="/Super/Manipuler" method="post">
<fieldset>
<legend>Formulaire d'ajout d'un administrateur</legend>
#Html.Label("Login")
#Html.Label("Mail")
#Html.Label("Password")
#Html.Label("Name")
<br />
<br />
#if(Model != null){
foreach (Upload.Models.AdminModels admin in Model)
{
if (i == 0){
<input type="radio" checked class="radio" name="radio" value="#admin.Login" >
}
else{
<input type="radio" class="radio" name="radio" value="#admin.Login" style="margin-left:0.3px;">
}
<label id="log">#admin.Login</label>
<label id="logm">#admin.Mail</label>
<label id="logp">#admin.Password</label>
<label id="logn">#admin.Name</label>
<br />
i++;
}
}
<br />
<input type="submit" value="Editer" name="submit_button"/>
<input type="submit" value="Supprimer" name="submit_button" />
Créer un nouveau compte
</fieldset>
</form>
</section>
In the controller : the action Manipuler is the below:
public ActionResult Manipuler()
{
string buttonName = Request.Form["submit_button"];
string _login = Request.Params["radio"];
Upload.Models.AdminModels admin = new AdminModels();
Upload.Models.CompteModels.Modifiying_login = _login;
if (buttonName == "Editer") { return RedirectToAction("Edit", "Admin"); }
else { admin.Delete_admin(_login); return RedirectToAction("Home", "Super"); }
}
It's works fine but i'd like to change the radiobox to checkbox.
My question is how to know all checked box in the collection of checkbox in the action Manipuler ?
Take a look at Phil Haack's article on model binding a checkbox list. Basically, you just need to set up the HTML in a specific way (name your checkboxes the same which will then convert the various POSTed values into a list).
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Categories

Resources