MVC3 Checkbox value won't clear - c#

I have a fairly straightforward razor view which needs to be used for a questionnaire, which is used repeatedly until it runs out questions. Checkbox is used for select answers, the problem is when I first submit the form when the next set of questions returned the answers I submit in the first time are retained in the second page. For the simplicity I have reduced the program as follows,
Model,
public class CheckBoxItemDto
{
public int Id { get; set; }
public bool Selected { get; set; }
}
public class CheckBoxModel
{
public CheckBoxModel()
{
Dtos = new List<CheckBoxItemDto>();
}
public IList<CheckBoxItemDto> Dtos { get; set; }
}
Controller,
public class CheckBoxController : Controller
{
public ViewResult Index()
{
CheckBoxModel model = new CheckBoxModel();
for (int i = 0; i < 5; i++)
{
model.Dtos.Add(new CheckBoxItemDto(){Id = i,Selected = true});
}
return View(model);
}
[HttpPost]
public ViewResult Index(CheckBoxModel mdl)
{
CheckBoxModel model = new CheckBoxModel();
for (int i = 5; i < 10; i++)
{
model.Dtos.Add(new CheckBoxItemDto() { Id = i, Selected = i % 2 == 0 });
}
return View(model);
}
}
View,
#model CheckBoxTest.Models.CheckBoxModel
#{
ViewBag.Title = "ViewPage1";
}
<h2>ViewPage1</h2>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Dtos.Count; i++)
{
#Html.DisplayFor(m => m.Dtos[i].Id)
#Html.DisplayFor(m => m.Dtos[i].Selected)
#Html.CheckBoxFor(m => m.Dtos[i].Selected)
<br/>
}
<input type="submit" value="Index" />
}
In the view after post, DisplayFor and CheckBoxFor column values are different. But It should be the same.

By the sound of it your ModelState is still holding its previous values when you return the view. Try clearing the ModelState in your post action:
ModelState.Clear();
CheckBoxModel model = new CheckBoxModel();
...

In your Index action method that gets run for a POST, ASP.Net MVC is expecting the resulting View to display validation errors. Therefore, the HTML Helper method CheckBoxFor looks in the ModelState before the Model, so it can display the "invalid" value to the user.
If you're not using the View from a POST action to display errors, you can clear the ModelState as Dangerous suggests. Or, you could use the "Post-Redirect-Get" pattern: in your POST action method, display the same View if there are errors, or else redirect to a GET view that displays something else.

Related

View providing the same value to Controller

I'm trying to write a simple numbers checking program. You enter a correct next digit - it shows you next one, if not - nothing happens. In the Controller I'm trying to: set a value of variable, increment it, pass to specific View (howManyDigits). I know that I can make howManyDigits a session variable but the point here is to understand why the number is still going back to eg. "2" which was entered in View1 just after the first run of the app.
PiController:
namespace PI.Controllers
{
public class PiController : Controller
{
[HttpGet]
public ActionResult View1()
{
return View();
}
[HttpPost]
public ActionResult View2(Pinumber number)
{
if (number.tabPi[number.howManyDigits] != number.numberEntered)
number.howManyDigits++;
return View(number);
}
}
}
View1:
#model PI.Models.Pinumber
#using (Html.BeginForm("View2", "Pi", FormMethod.Post))
{
#Html.TextBoxFor(number => number.howManyDigits,new { autofocus = "autofocus"})
<input type="submit" value="How many numbers do you know?" />
}
In this view I have BeginForm and i'm trying to pass this variable using HiddenFor and Lambda expression back to the same Controller.
View2:
#model PI.Models.Pinumber
<div>
3.#for (int i = 0; i < Model.howManyDigits; i++)
{
#Model.tabPi[i]
}
</div>
#using (Html.BeginForm("View2", "Pi", FormMethod.Post))
{
#Html.HiddenFor(x=> x.howManyDigits)
#Html.TextBoxFor(x => x.numberEntered, new { autofocus = "autofocus" })
}
Model class Pinumber:
namespace PI.Models
{
public class Pinumber
{
public char[] tabPi { get; set; } = new char[100000];
public Pinumber()
{
for (int i = 0; i < 20; i++)
{
tabPi[i] = piNumber[i];
}
}
public int howManyDigits { get; set; }
public char numberEntered { get; set; }
public string piNumber = "141592653589793238462"
Unfortunately it's going back to Controller with an old value which i entered in other View1 which is used just to display the start site with entry number of digits that you already know - [HttpGet].
Things are not working that way.
Look, if you increment an value of some int variable, from 1 to 2 let's say, and you pass it to view. Then you want to increment it from 2 to 3, you need to send that 2 from View(from HTML code) back to the backend, and increment it there, otherwise, value is lost.

How To Set CheckBoxFor Values Correctly Based On ViewModel Change?

My view model contains list of items. The list items are displayed by #Html.CheckBoxFor for user interaction. Initial set-up (i.e. checking values last selected in previous session) as well as manual selection and de-selection work correctly and bind back to the view model.
Function "Uncheck All" was added to the page recently. It sets each list item value IsSelected to false, but all items remains unchanged on the page.
When checking the ModelState.Values, the AttemptedValue of corresponding items got changed to "true\false" instead of "false". Am I missing any binding operation?
Quick summary of the main code parts:
View Model:
public class RuleDetail
{
public List<DataSourceCheckList> dataSourceCheckList { get; set; }
// many other items, methods etc...
}
View code:
#for (int n = 0; n < Model.dataSourceCheckList.Count; n++)
{
#Html.DisplayFor(model => model.dataSourceCheckList[n].DataSourceName)
#Html.HiddenFor(model => model.dataSourceCheckList[n].DataSourceName)
#Html.CheckBoxFor(model => model.dataSourceCheckList[n].IsSelected)
#Html.HiddenFor(model => model.dataSourceCheckList[n].DataSourceId)
}
The class from the list:
public class DataSourceCheckList
{
public int DataSourceId { get; set; }
public string DataSourceName { get; set; }
public bool IsSelected { get; set; }
public int columnCount = 0; //captures DataSetColumns count (0 migt mean the data source haven't been audited)
}
And the Post controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Builder(int? id, ViewModels.RuleDetail rd, string ignoreChecklists, string command, int? callerId, string callerController, string callerAction)
{
//some other logic...
switch (command)
{
//bunch of other command values
case "UnselectSources":
foreach (DataSourceCheckList i in rd.dataSourceCheckList)
{
i.IsSelected = false;
}
break;
}
//update of other view model fields based on actions above
return View(rd);
}
Please, do you see any options, how to ensure the values in RuleDetail.dataSourceCheckList are displayed correctly?
Issue was driven by model state. Applying ModelState.Clear() to [HttpPost] controller action eliminated the true\false state observed before and values updated during the post action (including the checkbox mentioned in the original questions) are displaying correctly now.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Builder(int? id, ViewModels.RuleDetail rd, string ignoreChecklists, string command, int? callerId, string callerController, string callerAction)
{
//some other logic...
switch (command)
{
//bunch of other command values
case "UnselectSources":
foreach (DataSourceCheckList i in rd.dataSourceCheckList)
{
i.IsSelected = false;
}
break;
}
//update of other view model fields based on actions above
ModelState.Clear();
return View(rd);
}

ASP EF Increment / Decrement Value with + / - buttons

So I'm new to ASP and EF and I am wondering how to do this incredibly basic operation, as well as a few questions to go along with doing it.
Currently I have a table we will call Resource;
class Resource
{
int current;
int min;
int max;
};
Right now I have the default CRUD options for this. What I would like is a + / - button on the main list that will manipulate the current value of each resource and update the value in the DB and on screen.
There are also certain operations I'd like to run such as "AddFive" to a selected group of resources.
So my questions;
How do I do this?
Is this scalable? If someone is constantly hitting the buttons this is obviously going to send a lot of requests to my DB. Is there any way to limit the impact of this?
What are my alternatives?
Thanks.
Edit:
To clarify the question; here are my post functions. How / where do I add these in my view to get a button showing for each resource. I just want the action to fire and refresh the value rather than redirect to a new page.
#Html.ActionLink("+", "Increment", new { id = item.ID })
public void Increment(int? id)
{
if (id != null)
{
Movie movie = db.Movies.Find(id);
if (movie != null)
{
Increment(movie);
}
}
}
[HttpPost, ActionName("Increment")]
[ValidateAntiForgeryToken]
public ActionResult Increment([Bind(Include = "ID,Title,ReleaseDate,Genre,Price")] Resource resource)
{
if ((resource.Current + 1) < (resource.Max))
resource.Current++;
return View(resource);
}
It sounds like the main issue you are having is creating a list of movies on the front end and updating the details for a specific one.
The key here is that you will need to either wrap a form around each item and have that posting to your update controller or use ajax / jquery to call the controller instead.
I have given you an example of the first one. Once the update controller is hit it will redirect to the listing page which will then present the updated list of movies.
Below is a minimal working example of how to wire this up. I've not included any data access code for brevity but have included psuedo code in the comments to show you where to place it.
Please let me know if you have any futher questions.
Controller
public class MoviesController : Controller
{
public ViewResult Index()
{
// Data access and mapping of domain to vm entities here.
var movieListModel = new MovieListModel();
return View(movieListModel);
}
public ActionResult Increment(IncrementMovieCountModel model)
{
// Put breakpoint here and you can check the value are correct
var incrementValue = model.IncrementValue;
var movieId = model.MovieId;
// Update movie using entity framework here
// var movie = db.Movies.Find(id);
// movie.Number = movie.Number + model.IncrementValue;
// db.Movies.Save(movie);
// Now we have updated the movie we can go back to the index to list them out with incremented number
return RedirectToAction("Index");
}
}
View
#model WebApplication1.Models.MovieListModel
#{
ViewBag.Title = "Index";
}
<h2>Some Movies</h2>
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Increment Value</td>
<td></td>
</tr>
#foreach (var movie in Model.MovieList)
{
using (Html.BeginForm("Increment", "Movies", FormMethod.Post))
{
<tr>
<td>#movie.Id #Html.Hidden("MovieId", movie.Id)</td>
<td>#movie.Name</td>
<td>#Html.TextBox("IncrementValue", movie.IncrementValue)</td>
<td><input type="submit" name="Update Movie"/></td>
</tr>
}
}
</table>
Models
public class MovieListModel
{
public MovieListModel()
{
MovieList = new List<MovieModel> {new MovieModel{Id=1,Name = "Apocalypse Now",IncrementValue = 3}, new MovieModel {Id = 2,Name = "Three Lions", IncrementValue = 7} };
}
public List<MovieModel> MovieList { get; set; }
}
public class MovieModel
{
public int Id { get; set; }
public string Name { get; set; }
public int IncrementValue { get; set; }
}
public class IncrementMovieCountModel
{
public int IncrementValue { get; set; }
public int MovieId { get; set; }
}

Using RenderPartial to assign Model Property values

I am new to MVC platform and trying a simple application to get hold of the framework,
Application:I am designing an admin application which has a form to enter question and multiple options to the database, I have a viewmodel called "QuestionViewModel" which has properties "Quesiton" and a List of OptionsViewModel in it, "OptionViewModel" consists of "Option" and "IsRightAnswer" properties, so on the UI, I have a QuestionView which displays a textbox for Question and I want a question to have 4 options, so I created a PartialView called "QuestionOptionView" which takes "OptionViewModel" as its model,
now I loop about 4 times through the PartialView and display a TextBox for "Option" and "IsRightAsnwer" radiobutton,
The UI displays TetsBox for "Question" and 4 other "TextBoxes" for entering the "Options",
but when I Post this form, the "Options" does not bind to the ViewModel
How can I achieve Model Binding from these partialview data to the main viewmodel??
public class QuestionViewModel
{
[Display(Name = "Enter a question")]
public string Question { get; set; }
public IList<QuestionOptionViewModel> Options { get; set; }
public int MaxOptions { get { return 4; } }
public QuestionViewModel()
{
Options = new List<QuestionOptionViewModel>();
}
}
public class QuestionOptionViewModel
{
public string Option { get; set; }
public bool IsRightAnswer { get; set; }
}
In the viewm I have as below,
#using (Html.BeginRouteForm("savequestion", new {}, FormMethod.Post)){
<div>
#Html.LabelFor(m => m.Question)
#Html.TextBoxFor(m => m.Question)
</div>
<p>Add options for the question</p>
for (int i = 0; i < Model.MaxOptions; i++)
{
{ Html.RenderPartial("QuestionOption", new Babbi_Test_admin.Models.QuestionOptionViewModel()); }
}
<input type="submit" value="Save" />
When the form is submitted I have my post method as,
[HttpPost]
public void SaveQuestion(QuestionViewModel viewModel)
{
}
My viewModel in the post has "Options" as null
This is happening cause you must be binding the QuestionViewModel with the Action of your Controller instead, you need to create a ViewModel that will have the QuestionViewModel OptionViewModel
Error
[HttpPost]
public void SaveQuestion(QuestionViewModel viewModel)
{
}
In the Post Method you are expecting QuestionViewModel then how can you expect to get the OptionviewModel
Another thing your are Renbdering Partial View for QuestionOptionViewModel which Contains Options and RIght Answer so in the loop you will get the Right Answer 4 thing which is incorrect.
Sample
public class QuestionViewModel
{
//Properties
}
public class OptionViewModel
{
//Properties
}
public class QuestionAndOptionViewmodel
{
// QuestionViewModel and OptionViewModel model object
//Constructors
//Methods
}
[HttpPost]
public void SaveQuestion(QuestionAndOptionViewmodel viewModel)
{
//Your Code Here;
}
Try changing your code to:
for (int i = 0; i < Model.MaxOptions; i++)
{
{ Html.RenderPartial("QuestionOption", Model.Options.Item(i)); }
}
Or even better:
foreach(option in Model.Options)
{
Html.RenderPartial("QuestionOption", option);
}
You will need to initialize the option values in your Question Model constructor

How Do I carry dropdown value selected in Asp.net MVC

Hi I have Dropdown in Index page where user needs to select lists. Values are coming from Database. I took this dropdown value into session so I can carry this to Httppost.
Below is my code in Index page :
var activitydropdown = orderdata.uspApp_ActivityPageReportname(Convert.ToInt32(newid)).ToList();
List<SelectListItem> activitypage = new List<SelectListItem>();
if (activitydropdown != null && activitydropdown.Count > 0)
{
foreach (var activityresults in activitydropdown)
{
activitypage.Add(new SelectListItem
{
Text = activityresults.name,
Value = activityresults.id.ToString(),
});
}
}
ViewData["activitydropdown"] = activitypage;
Session["activitydropdown"] = activitypage;
And this is my code in view :
#using (Html.BeginForm("Index", "Automation", new { step = "2" }, FormMethod.Post, new { id = "frmIndex" }))
{
#Html.DropDownList("DrpaActivity", ViewData["activitydropdown"] as List<SelectListItem>, "All", new { style = "margin-left:694px;margin-bottom:20px;", onchange = "submit();" })
Now when user selects list from dropdown, i need to carry that text to my httpost index. Now in httpost index, in debug mode if i see this code :
var sessionlistautomation = Session["activitydropdown"];
I can see text and value and selected is false for every item. So how can i carry text here selected from Index to httpost, so when user selects list from dropdown, it stores that text value.
It will be available in your Request i.e.
Request["DrpaActivity"]
However I would strongly advise using ViewModels instead as they're typesafe, less room for error and easier to use.
If you create a view model, like below:
public class AViewModel
{
public string DrpaActivity { get; set; }
public List<SelectListItem> ActivitySelectList { get; set; }
}
In your Index you can return it like this:
public ActionResult Index()
{
var model = new AViewModel();
// set the select list i.e.
model.ActivitySelectList = // get from db etc
return View(model);
}
Then in your view declare the model at the top
#model AViewModel
...
Set your dropdown like this:
#Html.DropDownListFor(m => m.DrpaActivity, Model.ActivitySelectList as List<SelectListItem>, "All", new { style = "margin-left:694px;margin-bottom:20px;", onchange = "submit();" })
You can then get your selected drop-down in your post as follows:
[HttpPost]
public ActionResult Index(AViewModel model)
{
var isValid = model.DrpaActivity;
return View(model);
}

Categories