Controlling default values in view from model - EF CORE - asp.net-core-mvc

I've set the default for a Boolean in the model using both
[DefaultValue(true)]
and
public bool IsActive { get; set; } = true;
and for good measure I've added
modelBuilder.Entity<modelName>().Property(p => p.IsActive).HasDefaultValue(true);
That all works nicely and my database table has the correct "Default value or binding". In my Create view I want the default property to be used but can't find out how to use the model's default properties in the view. Using
<input asp-for="IsActive" class="form-control" checked="checked"/>
works, but that's me hardcoding the checked="checked" value myself. How do I read the model's default value in the view?
Edited for requested code, the model is
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace securitytrials.Models {
public class CaseType {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TheID { get; set; }
[Display(Name = "Active")]
public bool IsActive { get; set; } = true;
}
}
the action method is the default generated by the template.
public IActionResult Create() {
return View();
}
Didn't include them as there's not much there but the defaults.
The DbContext.OnModelCreating is just
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<CaseType>().Property(p => p.IsActive).HasDefaultValue(true);
}

DefaultValue doesn't actually do anything. I can be utilized by code generators in certain situations, but largely in the context of your web application, it's completely meaningless. As the docs detail:
A DefaultValueAttribute will not cause a member to be automatically initialized with the attribute's value. You must set the initial value in your code.
HasDefaultValue merely sets the DEFAULT for the column, so you may need that as well, if you want the actual table schema to reflect the default, but the only thing that really helps your code is:
public bool IsActive { get; set; } = true;
It's not entirely clear what you're asking. As there should be no issue with that alone in an input displaying that default value. In other words, the following code:
<input asp-for="IsActive" class="form-control" />
Will generate:
<input type="textbox" class="form-control" checked="checked" />
If it's not, then there's some issue with your model. Either you're confused on which class you've added the default value to, or something else in your code is changing that value before it hits your view. However, without more code to work with, like your action method, it's impossible to say.

Related

I have a complex View Model that has built complex named html elements. How do I build a matching post model?

I have a lot of data that needs to be passed from a controller to a view and I am trying to use strongly typed View Models where possible.
Take an example where you have a database of loads of people - We want to edit a person whilst also presenting a list of everyone with the same surname.
public class person
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class testviewmodel
{
public List<person> people { get; set; }
public person newperson { get; set; }
}
I can't use testviewmodel as the model for the post because there is a lot more going on in the form/data. I have managed to build a model that contains nearly all the form data, other than the ones from the View Model.
I generate some items in the form via:
<input asp-for="newperson.Firstname" class="form-control"/>
This in return generates:
<input class="form-control" disabled type="text" id="newperson_Firstname" name="newperson_Firstname" value="xxxx" />
However, I have tried adding newperson_Firstnameto my model alongside quite a few other combinations, and, I am just not able to see the data.
Can anyone please assist and let me know what I am doing wrong - or, should I just be adjusting the view model to be more fit for purpose?
...Lastly, is there any equivalent of var_dump($_REQUEST);? At the moment, I'm adding breakpoints and trying to open up different items within Locals, but, it's trial and error and taking ages... I'm just trying to find where the form is!
You shouldn't need to dig around in the Request object. If you pass an instance of your ViewModel to your post action, model binding will take care of populating the Person property automatically:
[HttpPost]
public IActionResult Edit(TestviewModel model)
{
var person = model.Person; // add a breakpoint here, should represent the posted values
}

Required field not present on all pages leading to problems

I have a ‘Create’ page in my MVC3 application that has 4 input fields that are all required. I also have an ‘Edit’ page in which 3 of these 4 fields can be edited. I do not want to display the 4th field and want to maintain it at its initial value (the field is the date that the entry was created ).
I mark the 4th field as [Required] in the model then this causes the model to be declared as invalid in post action method of the Edit field. If I omit the [Required] annotation then someone can create a user with a null value for this 4th field.
How can I get around this problem?
Model Code:
[Required]
[DisplayName("User Name")]
public string UserName { get; set; }
[Required]
public string Role { get; set; }
[Required]
[DataType(DataType.Date)]
[DisplayName("Insert Date")]
public DateTime? InsertDate { get; set; }
[Required]
[DisplayName("Active")]
public bool ActiveInd { get; set; }
Controller Code:
public ActionResult Edit(int id, ZUserRoleModel mod)
{
if (ModelState.IsValid)
{
// code removed
return RedirectToAction("Index");
}
return View(mod);
}
You can make that field as hidden in edit mode.
#Html.HiddenFor(x => x.EntryDate)
Not sure if you still need an answer for this, but what you need to do in order for the
#Html.HiddenFor(x => x.EntryDate )
to work, is pass an existing model into view. So let's assume that your action for getting the user data looks like this. ( You did not supply it, so I am not sure if this is right )
Public ActionResult GetUser(int UserID)
{
ZUserRoleModel model = new ZUserRoleModel(UserID);
// Maybe this could go to your database and gather user
// It would populate the correct data into a model object
return View("Edit", model);
}
With combination of the hidden field, your view will be populated with the existing user information, and the hidden field will be populated with data, and it will be passed to your edit action.
NOTE: I wrote this without any kind of testing, but it should still work, or at the very least, I hope it points you in the right direction if you still need assistance.
You can use fluentvalidation: http://fluentvalidation.codeplex.com/
Have a rule that's something like
RuleFor(user => user.field4).NotEmpty().When(ViewContext.Controller.ValueProvider.GetValue("action").RawValue <> "edit")

asp.net mvc3 updated (refresh) the viewmodel in view

I send a BOOKVIEWMODEL with fields and a simple IEnumerable in view I get the this list IEnumerable in the view by a method with JSON AJAX in view and I fill my table Ristourne(View) with JQUERY it works very well but I not know how I fill (BIND or refresh) the list IEnumerable of my BOOKVIEWMODEL in the VIEW to recovered it in the Controller:
[HttpPost]
public ActionResult Create(BookViewModel _bookViewModel)
{
if (ModelState.IsValid)
{
_bookViewModel.Ristourne
return RedirectToAction("Index");
}
return View(_bookViewModel);
my bookviewmodel
public class BookViewModel
{
public String book { get; set; }
public String price { get; set; }
public IEnumerable<Ristourne> Ristourne { get; set; }
}
For the model binding to work, you need to "mimic" the convention MVC uses when generating the form fields.
I don't know the contents of the Ristourne class, but let's say it had 1 field called Foo.
In that case, when you render out the elements (from the JSON AJAX callback), make them look like this:
<input type="text" name="Model.Ristourne[0].Foo" id="Model.Ristourne[0].Foo"/>
<input type="text" name="Model.Ristourne[1].Foo" id="Model.Ristourne[1].Foo"/>
<input type="text" name="Model.Ristourne[2].Foo" id="Model.Ristourne[2].Foo"/>
And so on. Easiest thing to do is in your AJAX callback, just use a basic for loop to create the elements indexer.
Altenatively, a cheat/easy way around this problem would be to make your AJAX action return a PartialViewResult:
public PartialViewResult Book()
{
var ristournes = _repo.Get();
var model = new BooksViewModel { Ristourne = ristournes };
return PartialView(model);
}
Then the partial view:
#Html.EditorFor(model => mode.Ristourne)
Then MVC will create the form fields correctly.
I always prefer this option over dynamically generated form fields. If you want to go down this path often, you should consider something like Knockout.js and/or Upshot.

html.TextBoxFor and html.Textbox, POSTing values, model in parameters

Alright guys, Need some help!
Im working with asp.net mvc3 razor (and am fairly new to it but did lots of web forms)
Okay so onto the problem
My question revolves around submitting a view.
I have a very complicated model that my view is based off (strongly typed).
I want to return the model into the arguments in the HttpPost method of the controller. do basically:
public ActionResult Personal()
{
DataModel dataModel = new DataModel();
FormModel model = new FormModel();
model.candidateModel = dataModel.candidateModel;
model.lookupModel = new LookupModel();
return View(model);
}
[HttpPost]
public ActionResult Personal(FormModel formModel)
{
if (ModelState.IsValid)
{
//stuff
}
return View(formModel);
}
Now...
I'm having trouble getting values into the formModel parameter on the post method.
This works (meaning i can see the value)but is tedious as i have to write exactly where it sits in a string every single field:
#Html.TextBox("formModel.candidateModel.tblApplicant.FirstName", Model.candidateModel.tblApplicant.FirstName)
It renders like this:
<input name="formModel.candidateModel.tblApplicant.FirstName" id="formModel_candidateModel_tblApplicant_FirstName" type="text" value="Graeme"/>
This doesn't work:
#Html.TextBoxFor(c => c.candidateModel.tblApplicant.FirstName)
It renders like this:
<input name="candidateModel.tblApplicant.FirstName" id="candidateModel_tblApplicant_FirstName" type="text" value="Graeme"/>
Now I'm assuming the problem lies in the discrepancy of the id's
So please answer me this:
Am i going about this the right way
Why doesn't textboxfor get the right value/id, and how do i make it get the right value/id so i can retrieve it in a POST(if that is even the problem)?
Additionally, it seems that textboxfor is restrictive, in the manner that if you have a date time, how do you use the .toshortdate() method? This makes me think textboxfor isn't useful for me.
Quick clarification:
when i say textboxfor isn't working, it IS getting values when i GET the form. So they fill, but on the POST / submission, i can't see them in the formModel in the parameters.
Another side note:
None of the html helpers work, this is the problem. They aren't appearing in modelstate either.
Thanks everyone for the help
Answer:
html.TextBoxFor and html.Textbox, POSTing values, model in parameters
It was a problem in my view somewhere, i replaced all the code with the snippet in this answer and it worked.
Thank you again
Am i going about this the right way
Yes.
Why doesn't textboxfor get the right value/id, and how do i make it get the right value/id so i can retrieve it in a POST(if that is even the problem)?
There is something else in your code that makes this not work. It's difficult to say since you haven't shown all your code. Here's a full working example which illustrates and proves that there's something else going on with your code:
Model:
public class FormModel
{
public CandidateModel candidateModel { get; set; }
}
public class CandidateModel
{
public Applicant tblApplicant { get; set; }
}
public class Applicant
{
public string FirstName { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new FormModel
{
candidateModel = new CandidateModel
{
tblApplicant = new Applicant
{
FirstName = "fn"
}
}
});
}
[HttpPost]
public ActionResult Index(FormModel formModel)
{
// the username will be correctly bound here
return View(formModel);
}
}
View:
#model FormModel
#using (Html.BeginForm())
{
#Html.EditorFor(c => c.candidateModel.tblApplicant.FirstName)
<button type="submit">OK</button>
}
Additionally, it seems that textboxfor is restrictive, in the manner
that if you have a date time, how do you use the .toshortdate()
method? This makes me think textboxfor isn't useful for me.
I agree that TextBoxFor is restrictive. That's why I would recommend you always using EditorFor instead of TextBoxFor. It will allow you to simply decorate your view model property with the [DisplayFormat] attribute and voilà. You get any format you like.
For example:
public class MyViewModel
{
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreatedAt { get; set; }
}
and in the view:
#model MyViewModel
#Html.EditorFor(x => x.CreatedAt)
will format the date exactly as you expect.
the model binder uses the name to bind the values to the model, and the html helpers e.g. Html.TextBoxFor uses the body of the lambda expression to set the name, however you can specify the name yourself which you are doing by using the Html.TextBox( helper
#Html.TextBoxFor(x=>x.candidateModel.tblApplicant.FirstName),
new{#Name="formModel.candidateModel.tblApplicant.FirstName"})
If your view is strongly typed, try the helper bellow, instead call each helper on each property
#Html.EditorForModel()
#Html.EditorFor(m => m.candidateModel)
#Html.EditorFor(m => m.lookupModel)
Update:
Well, have tried to use viewmodel to simplify this task? And when you get back the data you can map your real models. keep your views clean will give you less headaches in the future. Additionally you could use AutoMapper to help you.
Here a example if you think that will help you.
http://weblogs.asp.net/shijuvarghese/archive/2010/02/01/view-model-pattern-and-automapper-in-asp-net-mvc-applications.aspx

MVC 3, NHIbernate Validators & Message Interpolator

I have followed this article and have a passing test showing custom validation error messages being returned from a resource file when a call to Validator.IsValid(someEntity) fails.
I am trying to translate this to the new unobtrusive client-side validation in MVC3 describe by this article. This also more or less works - well the client-side validation does so I can assume my wiring of NHValidators is good, as you can see the following is output:
<input data-val="true" data-val-required="{NotNullNotEmpty}" id="Username" name="Username" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Username" data-valmsg-replace="true">
My problem is that this time the CustomMessageInterpolator has not fired - the resource string has not been translated for this property (it's still in curly braces): {NotNullNotEmpty}.
Both my test and the web are usign the same NHValidator config:
var cfg = new FluentConfiguration();
cfg
.SetMessageInterpolator<CustomMessageInterpolator>()
.SetCustomResourceManager("MyProject.Core.Resources.ValidationMessages", Assembly.Load("MyProject.Core"))
.SetDefaultValidatorMode(ValidatorMode.UseAttribute)
.Register(Assembly.Load("MyProject.Core").ValidationDefinitions())
Just for good measure here's the model snippet:
public class LoginModel
{
[NotNullNotEmpty(Message = "{NotNullNotEmpty}")]
public string Username { get; set; }
Long shot I know but any ideas would be appreciated!
I had no luck with this having posted it around a few forums.
In the end I've resorted to using DataAnnotations instead in my View Models for client-side validation.
Of course this is really hacky and a horrible "solution" as I've now two types of validation attributes in my code. (if wondering, yes I'd rather keep my NH Validation attributes 1. as I'm still generating my schema from them and 2. I like the way NH validates before committing to the db i.e. server-side).
If interested, here's how to use resource files to show the correct labels for your controls.
This code snippet shows how to then also rig up custom error messages:
[DisplayResource(typeof(LabelResources))]
public class NewChildTagViewModel : ModuleViewModel
{
public int ParentId { get; set; }
[Required]
public string Title { get; set; }
[Range(1, 1000, ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = "Display_Order")]
[Required]
public int? DisplayOrder { get; set; }
}
Can you show your code? I presume you used some sort of custom ModelValidatorProvider?
I've got something working like what you described. It required writing a custom AssociatedValidatorProvider and ModelValidator, along the lines described here
http://weblogs.asp.net/srkirkland/archive/2010/07/20/nhibernate-client-validator-asp-net-mvc-2-model-validation.aspx
but changing the MessageOrDefault method in the article above to use the NHibernate Validator DefaultMessageInterpolator to translate the messages.
It sounds like you got most of the way towards getting something working, and you just needed to use the DefaultMessageInterpolator to translate the messages.

Resources