I am developing an ASP.Net MVC 3 Web Application using Razor Views. Within the View I would like to ask the user a question which can only have a Yes/ No answer, therefore, I am planning on using two Radio Buttons.
I have a ViewModel which I pass to my View, and it looks like this
public class ViewModelFormImmigration
{
public int immigrationID { get; set; }
public int formID { get; set; }
[Required(ErrorMessage = "Please select Yes or No.")]
public bool euNational { get; set; }
}
In my View I then have the following lines of code to display the radio buttons for both the Yes and No options
#Html.RadioButtonFor(model => model.euNational, true) <text>Yes</text>
#Html.RadioButtonFor(model => model.euNational, false) <text>No</text>
My problem is that, when the User comes to this View for the first time, I would like neither the Yes or No option selected. At the moment, when I create an instance of my ViewModel (see above), the property euNational is defaulted to false and when the ViewModel is passed to my View, this means that the option No is automatically selected.
Is there anyway around this so that when the User see's the View for the first time, that no option is not selected by default?
Thanks for your help everyone.
Try changing:
[Required(ErrorMessage = "Please select Yes or No.")]
public bool euNational { get; set; }
To:
[Required(ErrorMessage = "Please select Yes or No.")]
public bool? euNational { get; set; }
This way, when you first load the view with no value set for euNational it will not set to neither true or false. As at the moment when it has no value set, it defaults to false
Related
I am stuck with this problem.
I have a model AssessmentModel defined like this:
public class AssessmentModel
{
public Respondent Respondent { get; set; }
public List<CompetencyModel> Competencies { get; set; }
}
public class CompetencyModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ResultModel> Results { get; set; }
}
public class ResultModel
{
public int Id { get; set; }
public int Score { get; set; }
}
All I need is to set value to the Score property of ResultModel.
Score is the only editable property here.
And I have just 1 View only, this view has a #model List, it displays a list of CompetencyModel items with Edit button for each one.
When I click the Edit button, the Id of CompetencyModel is passed to the same View, and the View draws an Edit form for ResultModel items that belong to the selected CompetencyModel.
However the form for ResultModel items exists on the same View, and the model of the View is still #model List.
How can I get to the Score property by using bindable Html.EditorFor(m=>m.Score) helper for each ResultModel item?
The View is defined like this:
#model List<CompetencyModel>
#foreach(var comp in Model)
{
<p>#comp.Name</p>
Edit
}
In the controller I set ViewBag.CurrentId = comp.Id, and at the bottom of the View:
if(ViewBag.CurrentId != null) //draw a form for ResultModel items
{
// What should I do now?
// how cant I use Html.EditorFor(m=>...) if the Model is still List<CompetencyModel>
}
I need to get to a single ResultModel entity to set a value to a Score property.
Thank you.
You should be able to get this done using Linq. Consider having the following code segment in the your last if statement
var result = Model.Results.FirstOrDefault(r => r.Id == ViewBag.CurrentId);
I dont have a IDE with me, so watchout for syntext errors
I am building a simple ASP.NET MVC 3 site, using Entity Framework to build the model based on some tables in an already existing Oracle database (in other words, I used the 'database first' methodology to have my model built). I now have a simple blog type site, which I am quite familiar with as I have been learning MVC in a number of languages.
I want to change some of the auto-generated views. One piece I would like to change in particular is that I have a field in one of my tables called 'Visible'. This is simply a numeric value (0 or 1) indicating whether or not a display application should use the row as display data. Currently, I have the simple text field that is auto-generated:
<div class="editor-field">
#Html.EditorFor(model => model.VISIBLE)
#Html.ValidationMessageFor(model => model.VISIBLE)
</div>
What I would like to do is replace this with a drop down box with string values like True and False. The application should display any entry with a 0 as false and vice versa. If a user wants to flip the toggle, the drop down should allow them to do that and understand to make the numeric update when clicking submit. How can this be done?
I have seen countless examples where the drop-down was going to be filled with more then just two values, and in those cases I understand that you can add logic to your controller that pulls all the distinct values, puts them in a list, then adds the list to the ViewBag. However, in my case with only two possible numeric values, it seems like there should be a simpler, more accepted way to do it.
Any help is greatly appreciated.
UPDATE
Following Quinton's answer, I am trying to place said code in my model. Here is my current model:
namespace CurrentActivityBlog
{
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
public partial class TBLCURRENTACTIVITY
{
public string TITLE { get; set; }
public string DESCRIPTION { get; set; }
public System.DateTime DATETIME { get; set; }
public short VISIBLE { get; set; }
public decimal ID { get; set; }
public IEnumerable<SelectedListItem> PossibleValues { get; set; }
public TBLCURRENTACTIVITY() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" },
new SelectListItem { Value = "1", Text = "Visible" } };
}
}
}
I am unable to build this solution, but Visual Studio 2010 is telling me that
"the type or namespace name 'SelectedListItem' could not be found (are you missing a using directive or an assembly reference?)"
As you can see, I have
using System.Web.UI.controls
and have added the reference to System.Web. Is there anything I am forgetting, or anything I should know about (such as models generated using EF behaving differently then one might expect, etc.)?
Thanks again.
The one line solution to it would be:
#Html.DropDownList(Model.VISIBLE.ToString(), new [] {new SelectListItem { Value = "0", Text = "Hidden"}, new SelectListItem { Value = "1", Text = "Visible"}})
but you probably don't want domain logic in your view. So add the possible items to your Model (or from the controller):
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
public MyModel() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
}
}
and then your razor code:
#Html.DropDownList(Model.VISIBLE.ToString(), Model.PossibleValues)
Obviously "Hidden" and "Visible" descriptions can be replaced with "False" and "True" or whatever.
You could also create and Editor and Display Template for that specific field. Checkout ScottGu's blog post here, search for "UI Helper Templating Support" and you'll see how to create a editor template and how to render a specific template by name.
EDIT:
If your model is not part of your MVC project, then referencing any classes that are in the MVC assemblies would require a explicit add reference. You can avoid this though by initializing any MVC assembly types in your model from your controller, like such:
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
}
controller action method:
public ActionResult Edit(int Id) {
...
myModelInstance. PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
...
Return View(myModel);
}
I have two classes as follows
public class ODCTE_Major
{
public int ODCTE_MajorId { get; set; }
public string OfficialMajorName { get; set; }
public string MajorCode { get; set; }
... More unrelated code ....
}
AND
public class CareerMajor
{
...lots of unrealted code to this question left out
public int ODCTE_MajorId { get; set; }
public virtual ODCTE_Major ODCTE_Major { get; set; }
}
I added a controller with CRUD methods and in the create.cshtml there is this line
<div class="editor-field">
#Html.DropDownList("ODCTE_MajorId", String.Empty)
#Html.ValidationMessageFor(model => model.ODCTE_MajorId)
</div>
The select list populates it with the OfficialMajorName from ODCTE_Major. I need the select list to populate with the MajorCode or a value that looks like MajorCode - OfficialMajorName.
Could someone provide assistance for how this is done, please?
Thanks.
Add this to ODCTE_Major:
public string MajorDisplayName
{
get { return string.Format("{0} - {1}", MajorCode, OfficialMajorName); }
}
This is just a read only property used to create the display text in the format you want the menu to use.
Then in CareerMajor, add:
public IEnumerable<ODCTE_Major> Majors{ set; get; } // Thank you Shyju!
This will give you a place in your view model to pass the list of Majors you want in your menu to the view.
Then in your action method when you're creating a CareerMajor view model to send to the view, populate the new IEnumberable with the ODCTE_Major entities you'd like displayed in your menu.
On the view page:
#Html.DropDownListFor(m => m.ODCTE_MajorId, new SelectList(Model.Majors, "ODCTE_MajorId", "MajorDisplayName", Model.ODCTE_MajorId), "Select One")
This creates a SelectList to populate the drop down with. The SelectList constructor is saying use ODCTE_MajorId as the value for a SelectListItem in the menu, and to use MajorDisplayName as the text to actually display in the menu. It sets the selected value, if there is one, and adds a null item with the text "Select One" to the top of the menu. Feel free to take that final argument out if you don't want the null text.
Have your ViewModel hold a Collection property to represent all available Majors (for poulating the Dropdown)
public class CareerMajor
{
//other proiperties
public int ODCTE_MajorId { get; set; }
public IEnumerable<ODCTE_Major> Majors{ set; get; }
}
And in your GET Action, fill it and send it to your strongly typed view
pubilc ACtionResult Create()
{
var viewModel=new CareerMajor();
viewModel.Majors=db.GetllAllMajors(); // getting a list of ODCTE_Major objects
return View(viewModel);
}
and in the View, use the DropDownListFor HTML Helper method.
#model CareerMajor
#Html.BeginForm())
{
#Html.DropDownListFor(m=>m.ODCTE_MajorId,
new SelectList(Model.Majors,"ODCTE_MajorId ","MajorCode"),"select one..")
//other elements
}
In your controller action:
ViewBag.ODCTE_MajorId = new SelectList(availableMajors, "ODCTE_MajorId", "MajorCode");
*second and third parameters are the names of the value and text fields respectively
Then in your view:
#Html.DropDownList("ODCTE_MajorId", String.Empty)
where availableMajors is an IEnumerable that contains the majors you want to list.
I'm experiencing very odd behavior in the way an ASP.NET MVC3 view model is emitted -- for one field, ModelMetadata is not propagated. I'm using the templated helpers after Brad Wilson, though updated for Razor. Here's my view model:
public class FamilyBaseViewModel : ViewModelBase
{
[Display(Order = 10)]
public string FamilyName { get; set; }
[Display(Order = 30)]
[StringLength(50, ErrorMessage = "Street name can only be 50 characters long.")]
public string Street { get; set; }
}
public class FamilyPrivateViewModel : FamilyBaseViewModel
{
[Display(Name = "Date Started", Order = 20)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
[DataType(DataType.Date)]
public DateTime? DateStarted { get; set; }
}
The object.cshtml template runs through the properties and uses Html.Display to show them:
// object.cshtml
<ol>
#foreach (var prop in
ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForDisplay
&& !ViewData.TemplateInfo.Visited(pm)
&& pm.ModelType != typeof(System.Data.EntityState)))
{
<li>
#Html.Display(prop.PropertyName)
</li>
}
</ol>
In the above scenario, all three fields have the right descriptors in the object.cshtml call (prop.DisplayName, prop.TemplateHint), but when the first property -- FamilyName -- is passed to String.cshtml, the ViewData.ModelMetadata is not populated at all. As a result, the template can't display a label (except "String"), nor assign the ID of the control, etc.
Street and DateStarted are emitted normally, with the ID and all. So I'm completely at a loss as to why the one property would fail to set the ViewData properties -- nor do I know how to step through past the Html.Display call to see what might be happening.
Any ideas for a next place to look?
So the problem was in the controller action, which for unrelated reasons used "FamilyName" for a ViewData value:
ViewBag.FamilyName = familyName;
And this caused all heck to break loose in the mapping of model fields with the same name -- that is, ModelMetadata will not propagate. So, the lesson is: don't give ViewData dictionary items keys with the same name as a field in your view model.
I am using strongly typed views in an MVC3 web app. I've noticed that when a form is submitted, the ViewModel that is passed to the controller only has values for properties that have form elements associated with them. For instance, the example below shows a simple confirmation View with a checkbox and a phone number that the user must confirm before proceeding. When the form is submitted to the controller action, the UserConfirmed property contains a value, but the PhoneNumber property is null.
Is there any way for the ViewModel to retain all of its values or do I have to repopulate the ViewModel properties that do not have form elements associated with them?
The View
#model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
#Html.CheckBoxFor(model => model.UserConfirmed)
<span>Please confirm before proceeding</span>
<div>
Phone Number: #Model.PhoneNumber
</div>
<input type="submit" value="Confirm"/>
The Controller
[HttpPost]
public ActionResult ScheduleConfirmation(ScheduleConfirmationViewModel model)
{
if (model.UserConfirmed)
{
// add ViewModel data to repository
}
else
{
ModelState.AddModelError("ERROR", WebResources.strERROR_ConfirmSchedule);
}
return View(model);
}
Since your writing the phonenumber as output to the page it won't be automatically posted back (you've found out that part) What you can do is populate an hidden or read-only field with the phonenumber so that it will be posted back to your controller. An second option is to make a new call to your datasource and repopulate your object before saving it back to your datasource.
I generally POST back information like this in a hidden input. I personally use this heavily to pass data needed to return the user exactly where they where before pressing edit.
In your case it's as simple as
#model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
#Html.CheckBoxFor(model => model.UserConfirmed)
<span>Please confirm before proceeding</span>
<div>
#Html.HiddenFor(m => m.PhoneNumber)
Phone Number: #Model.PhoneNumber
</div>
<input type="submit" value="Confirm"/>
For future reference:
If your passing complex objects back you need one hidden field per attribute (Hiddenfor does NOT iterate)
View
WRONG
#Html.HiddenFor(m => m.PagingData)
RIGHT
#Html.HiddenFor(m => m.PagingData.Count)
#Html.HiddenFor(m => m.PagingData.Skip)
#Html.HiddenFor(m => m.PagingData.PageSize)
Action
public HomeController(AViewModel Model)
{
PagingData PagingData = Model.PagingData;
Skip = PagingData.Skip;
}
If your passing Arrays you can do it like this
View
#if (Model.HiddenFields != null)
{
foreach (string HiddenField in Model.HiddenFields)
{
#Html.Hidden("HiddenFields", HiddenField)
}
}
Action
public HomeController(AViewModel Model)
{
String[] HiddenFields = Model.HiddenFields;
}
Well, the form will only POST elements that you have created. As you found out, simply writing the phone number out to the page will not suffice. The model binder can only bind those properties which exist in the posted data.
Generally you have a couple of options here:
1) You can create Input elements for all of the properties in your model, using visible elements (like a textbox) for those properties you want to edit, and hidden elements which should be posted back but have no UI
2) Post back a partial representation of your model (as you are doing now), read the entity back in from it's data source (I assume you're using some kind of data source, EF maybe) and then alter the properties of that entity with the ones from your form.
Both scenarios are common but it really depends on the complexity of your model.
I know this thread is a bit old, but thought I'd resurrect it to get feed back on my solution to this.
I'm in a similar situation where my objects are passed to a view, and the view may only display part of that object for editing. Obviously, when the controller receives the model back from the default model binder, and values not posted back become null.. and saving this means that a DB value becomes null just because it wasn't displayed/returned from a view.
I didn't like the idea of creating a model for each view. I know it's probably the right way... but I was looking for a reusable pattern that can be implemented fairly quickly.
See the "MergeWith" method... as this would be used to take a copy of the object from the database and merge it with the one returned from the view (posted back)
namespace SIP.Models
{
[Table("agents")]
public class Agent
{
[Key]
public int id { get; set; }
[Searchable]
[DisplayName("Name")]
[Column("name")]
[Required]
[StringLength(50, MinimumLength = 4)]
public string AgentName { get; set; }
[Searchable]
[DisplayName("Address")]
[Column("address")]
[DataType(DataType.MultilineText)]
public string Address { get; set; }
[DisplayName("Region")]
[Searchable]
[Column("region")]
[StringLength(50, MinimumLength = 3)]
public string Region { get; set; }
[DisplayName("Phone")]
[Column("phone")]
[StringLength(50, MinimumLength = 4)]
public string Phone { get; set; }
[DisplayName("Fax")]
[Column("fax")]
[StringLength(50, MinimumLength = 4)]
public string Fax { get; set; }
[DisplayName("Email")]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[Column("email")]
[StringLength(50, MinimumLength = 4)]
public string Email { get; set; }
[DisplayName("Notes")]
[Column("notes")]
[DataType(DataType.MultilineText)]
public string Notes{ get; set; }
[DisplayName("Active")]
[Column("active")]
public bool Active { get; set; }
public override string ToString()
{
return AgentName;
}
public bool MergeWith(Agent a, string[] fields)
{
try
{
foreach (PropertyInfo pi in this.GetType().GetProperties())
{
foreach (string f in fields)
{
if (pi.Name == f && pi.Name.ToLower() != "id")
{
var newVal = a.GetType().GetProperty(f).GetValue(a,null);
pi.SetValue(this, newVal, null);
}
}
}
}
catch (Exception ex)
{
return false;
//todo: Log output to file...
}
return true;
}
}
}
And to use this in the controller.. you'd have something like..
[HttpPost]
public ActionResult Edit(Agent agent)
{
if (ModelState.IsValid)
{
Agent ag = db.Agents.Where(a => a.id == agent.id).ToList<Agent>().First<Agent>();
ag.MergeWith(agent, Request.Params.AllKeys);
db.Entry(ag).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(agent);
}
This way, during post back, it takes the object from the database, and updates it with object from view... but only updates the values that were posted back.. So if you have a field like "address" or something that doesn't appear in the view.. it doesn't get touched during the update.
I've tested this so far and i works for my purposes, tho i welcome any feedback as I'm keen to see how others have overcome this situation. It's a first version and i'm sure it can be implemented better like through an extension method or something.. but for now the MergeWith can be copy/pasted to each model object.
Yes, Just place hidden fields in the form for those values which you are not using and want to return to server control.
Thanks