I'm actually working on form validation in Blazor project (0.8.0).
I have created a component called InputValidation. This component receive many parameters to test if a property value is correct according a condition we can set up.
#using System.Linq.Expressions;
#typeparam TItem
#if (!Valid)
{
<span id="#(Id)_validation" class="form-text text-danger">#Message</span>
}
#functions {
[Parameter]
string Id { get; set; }
[Parameter]
TItem Property { get; set; }
[Parameter]
Expression<Func<TItem, bool>> On { get; set; }
[Parameter]
string Message { get; set; }
[Parameter]
bool ActiveOnLoad { get; set; } = true;
internal bool Valid { get; set; }
bool Activated;
protected async override Task OnInitAsync()
{
Activated = ActiveOnLoad;
}
protected async override Task OnAfterRenderAsync()
{
Activated = true;
}
protected async override Task OnParametersSetAsync()
{
Valid = !On.Compile().Invoke(Property);
}
}
You can implement it on your parent component like this :
<InputValidation Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
I have coded a class that verifies that all InputValidation components have the property Valid at true.
#if (ViewModel.IsValid(this))
this represents the parent component.
The problem is... it's not working !
Here is the code of the validator :
public static class ModelValidator
{
public static bool IsValid<T, V>(this T viewmodel, V component) where T : IViewModel where V : ComponentBase
=> component.GetType().GetFields().OfType<InputValidation<T>>().All(x => x.Valid);
}
It's not working, I know, but even if we use Reflection (GetProperties, GetFields, GetMembers), it won't return any of the InputValidation members of the parent component.
My question is : is there a way to get all child components by using Reflection ? If yes, how to do it ?
I know that Blazor is still on early stage and I hope it will be released soon because it's a very pleasant technology !
Thank you for your responses !
You don't need reflection here (the InputValidation component is not a field in the parent, it is a component that will be rendered by the RenderTree).
You can capture a reference to each InputValidation component using the ref attribute.
<InputValidation ref="#InputValidationRef" Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
Normally this ref "InputValidationRef" would be a field, but you can, instead use a property with a custom setter to build a list (or whatever collection you like)
List<InputValidation> InputValidations = new List<InputValidation>();
InputValidation InputValidationRef { set => InputValidations.Add(value); }
So, each InputValidation will now be captured as a reference and the Property InputValidationRef will be set for each one, which will, in turn, store all the references in the collection "InputValidations".
Now, you have a collection, you can test against
InputValidations.All(iv => iv.Valid)
Note: the collection is only populated after the component/page is rendered, so during the initial page load the collection of references is empty until the OnAfterRender/OnAfterRenderAsync method is called.
Related
I am working on Blazor project (.NET 5). I got a problem with components rendering.
I have parent component with ChildContent as RenderFragment inside. And I use it like this:
<ParentComponent>
<ChildComponent1 Title="Component1"></ChildComponent1>
<ChildComponent2 Title="Component2" SampleEnum="SampleEnum.Bar"></ChildComponent2>
</ParentComponent>
Each ChildComponent inherits ChildComponentBase:
public class ChildComponent1 : ChildComponentBase
{
// some code
}
ChildComponentBase contains ParentComponent as cascading parameter and 2 parameters: one of them is string (Immutable for Blazor Change Detection API) and another one is enum (which is not Immutable) just for sake of example. And here we also
public partial class ChildComponentBase
{
[CascadingParameter]
public ParentComponent Parent { get; set; } = default !;
[Parameter]
public string? Title { get; set; } // Immutable
[Parameter]
public SampleEnum SampleEnum { get; set; } // not Immutable
}
In ParentComponent I use a strategy of deferred rendering. Defer component looks like this and being used in ParentComponent:
// This is used to move its body rendering to the end of the render queue so we can collect
// the list of child components first.
public class Defer : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
protected override void BuildRenderTree( RenderTreeBuilder builder )
{
builder.AddContent( 0, ChildContent );
}
}
In my project on first render I collect all ChildComponent from ChildContent like this:
ChildComponentBase.razor
#{
Parent.AddChild(this); // Parent is cascading parameter
}
And then I invoke a callback to process data. ParentComponent looks like this:
ParentComponent.razor
<CascadingValue Value="this" IsFixed>
#{
StartCollectingChildren();
}
#ChildContent
<Defer>
#{
FinishCollectingChildren();
ProcessDataAsync();
}
#foreach (var o in _childComponents)
{
<p>#o.Title</p>
}
</Defer>
</CascadingValue>
ParentComponent.razor.cs
public partial class ParentComponent
{
[Parameter]
public RenderFragment ChildContent { get; set; }
private List<ChildComponentBase> _childComponents = new();
private bool _firstRender = true;
private bool _collectingChildren; // Children might re-render themselves arbitrarily. We only want to capture them at a defined time.
protected async Task ProcessDataAsync()
{
if (_firstRender)
{
//imitating re-render just like it would be an async call
await InvokeAsync(StateHasChanged);
_firstRender = false;
}
}
public void AddChild(ChildComponentBase child)
{
_childComponents.Add(child);
}
private void StartCollectingChildren()
{
_childComponents.Clear();
_collectingChildren = true;
}
private void FinishCollectingChildren()
{
_collectingChildren = false;
}
}
Due to invoke of callback - re-rendering happens. And due to re-rendering StartCollectingChildren() is getting called again. This time on second render of ParentComponent the ChildComponent1 doesn't re-render, because Blazor Change Detection API skips it (because it contains only an Immutable parameter Title while ChildComponent2 in addition contains enum parameter).
Question: how to make this ChildComponent1 get re-rendered anyway?
I also added a Sample Project with code described above for you to try it out yourself.
I tried everything I could find in the google. The best workaround I found is to cache children collection on first render, but it looks dirty and could cause issues in a future.
The quick fix to your problem is to modify the cascade and remove IsFixed.
Once you do that any component that captures the cascade will always be rendered because this is an object and therefore fails the equality check.
You can also drive render events on sub components that don't have object parameters using a Guid Cascade. Assign a new Guid to the mapped parameter whenever you want to force a render on any sub component the captures the cascade.
I have a component that accepts an int parameter - which the component uses to make an API call to retrieve some data. This logic is currently in the component's OnParametersSetAsync().
This component also has a complex-typed parameter.
When this component is used by a parent component that re-renders itself for various reasons, OnParametersSetAsync() is called on this child component - even if none of its parameters have changed. My understanding is that this is because of the complex-type parameter (blazor can't tell if it changed, so it assumes it did).
This results in the API call retriggering needlessly (the actual int parameter didn't change).
Is doing data-retrieval like this not appropriate for OnParametersSetAsync()? If so, how should I change my components to work with the Blazor framework?
Parent Component
Call to ChangeName() triggers the re-render of the parent
<div>
<EditForm Model="favoriteNumber">
<InputSelect #bind-Value="favoriteNumber">
<option value="0">zero</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
</InputSelect>
</EditForm>
#* This is the child-component in question *#
<TestComponent FavoriteNumber="favoriteNumber" FavoriteBook="favoriteBook" />
<br />
<EditForm Model="person">
First Name:
<InputText #bind-Value="person.FirstName" />
<br />
Last Name:
<InputText #bind-Value="person.LastName" />
</EditForm>
<button #onclick="ChangeName">Change Name</button>
</div>
#code {
private int favoriteNumber = 0;
private Book favoriteBook = new();
private Person person = new() { FirstName = "Joe", LastName = "Smith" };
private void ChangeName()
{
person.FirstName = person.FirstName == "Susan" ? "Joe" : "Susan";
person.LastName = person.LastName == "Smith" ? "Williams" : "Smith";
}
}
Child Component
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
protected override async Task OnParametersSetAsync()
{
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
}
}
which the component uses to make an API call to retrieve some data.
Your child component should not perform any API calls. It is the parent component that should manage the state of the parent itself and its children, downstreaming the data. If things get complicated, then you'll have to implement a service that handle state. #Peter Morris would certainly advise you to use Blazor State Management Using Fluxor.
Not sure why you use two EditForm components, when you actually should use none. Realize that components are very expensive, and they make your code slow. So use it wisely
Answering your question:
Define a local field to hold the FavoriteNumber parameter property's value as follows, in the child component:
#code
{
[Parameter]
public int FavoriteNumber { get; set; }
private int FavoriteNumberLocal = -1;
}
Note: The FavoriteNumberLocal variable stores the value passed from the parent component. It allows you to locally store and check if its value has changed, and accordingly decide whether to call the Web Api end point or not (Again, you shouldn't do that like this)
protected override async Task OnParametersSetAsync()
{
if( FavoriteNumberLocal != FavoriteNumber)
{
FavoriteNumberLocal = FavoriteNumber;
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id:
FavoriteNumberLocal.ToString());
}
}
Read the last two comments to this question
You can implement your own state logic with a private int.
A lot cheaper than calling an API again.
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
private int currentNumber = -1; // some invalid value
protected override async Task OnParametersSetAsync()
{
if (currentNumber != FavoriteNumber)
{
currentNumber = FavoriteNumber;
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
}
}
}
I don't think that this is necessarily a poor practice to put this logic into the OnParametersSetAsync() method. But there is a way to prevent it from making so many API calls. I would create a private variable that stores the value of the public parameter and then every time the OnParametersSetAsync() method is called you compare the two variables and if they are the same, then you don't make the API call, if they are different, then you make the API call, and after it finishes, you assign the private variable to the public parameter's value. To account for the very first time the component calls the method I would probably assign the private variable to default to a -1, as typically ID values aren't negative. But basically I would assign it to a value that would never be equal to any value passed as the parameter. Otherwise the first time it is called your API might not actually get called. Here is an example:
<div>#infoAboutFavoriteNumber</div>
#code {
[Parameter]
public int FavoriteNumber { get; set; }
private int CurrentFavoriteNumber { get; set; } = -1;
[Parameter]
public Book FavoriteBook { get; set; }
private string infoAboutFavoriteNumber = "";
protected override async Task OnParametersSetAsync()
{
if (FavoriteNumber != CurrentFavoriteNumber)
{
infoAboutFavoriteNumber = await ApiService.GetAsync<string>(id: FavoriteNumber.ToString());
CurrentFavoriteNumber = FavoriteNumber;
}
}
}
You can introduce local field and compare its value like other suggests or catch the old value before it changes in SetParametersAsync and it will work in basic scenarios.
However, what if:
the parameter changes too quickly? You will get concurrent requests and the response can come in wrong order.
you leave the page and the response come later?
you want to delay or throttle the parameter changes, e.g. when the parameter is bound to user input.
Reactive Extensions (IObservable) is designed to deal exactly with such scenarios. In Angular (very simmilar to Blazor), the RxJS is first class citizen.
In Blazor, just turn the parameter into IObservable, use RX Operators to deal with it without introducing your own local variables.
readonly Subject<Unit> _parametersSet = new ();
protected override Task OnParametersSetAsync()
{
_parametersSet.OnNext(Unit.Default); //turn OnParametersSetAsync into Observable stream
return base.OnParametersSetAsync();
}
[Parameter] public int FavoriteNumber { get; set; }
protected override void OnInitialized()
{
_parametersSet.Select(_ => FavoriteNumber) //turn parameter into Observable
.DistinctUntilChanged() //detect changes
.Select(value => Observable.FromAsync(cancellationToken =>
{
Console.WriteLine($"FavoriteNumber has changed: {value}");
infoAboutFavoriteNumber = await ApiService.GetAsync(value, cancellationToken);
})
.Switch() //take care of concurrency
.Subscribe();
}
The nice thing about it, that you can create a reusable class or helper method with all the boilerplate. You just specify a parameter and the async method, e.g:
Loader.Create(ObserveParameter(() => FavoriteNumber), LoadAsync);
for more reading, check:
this article: https://blog.vyvojari.dev/blazor-take-advantage-of-system-reactive-aka-observables-part-2/
live demo: https://blazorrepl.telerik.com/QQullYbo21fLFclq27
this question: How to add "reload" and IsLoading status to 2nd level Observable
You're facing a common problem: doing data and data access activity in the UI. Things tend to get messy! In this answer I've separated the data from the components. The data and data access reside in a Dependancy Injection service.
I've also done away with EditForm as you aren't actually using it, and changed the Select to a simple select so we can capture updates, update the model and trigger the data retrieval in the service. This also means that the component gets re-rendered after the model has updated. The Blazor UI event handler for the OnChanged event calls StateHasChanged after calling NumberChanged.
First a class for our Favourites data.
public class MyFavourites
{
public int FavouriteNumber { get; set; }
public string FavouriteNumberInfo { get; set; } = string.Empty;
}
Second a DI service to hold our Favourites data and datastore operations.
namespace Server;
public class MyFavouritesViewService
{
public MyFavourites Favourites { get; private set; } = new MyFavourites();
public async Task GetFavourites()
{
// Emulate a database get
await Task.Delay(100);
Favourites = new MyFavourites { FavouriteNumber = 2, FavouriteNumberInfo = "The number is 2" };
}
public async Task SaveFavourites()
{
// Emulate a database save
await Task.Delay(100);
// Save code here
}
public async Task GetNewNumberInfo(int number)
{
if (number != Favourites.FavouriteNumber)
{
// Emulate a database get
await Task.Delay(100);
Favourites.FavouriteNumberInfo = $"The number is {number}";
Favourites.FavouriteNumber = number;
}
}
}
Next register the Service in Program:
builder.Services.AddScoped<MyFavouritesViewService>();
The component:
<h3>MyFavouriteNumber is #this.Favourites.FavouriteNumber</h3>
<h3>MyFavouriteNumber info is #this.Favourites.FavouriteNumberInfo</h3>
#code {
[Parameter][EditorRequired] public MyFavourites Favourites { get; set; } = new MyFavourites();
}
And finally the page. Note I'm using OwningComponentBase to tie the scope of MyFavouritesViewService to the component lifecycle.
#page "/favourites"
#page "/"
#inherits OwningComponentBase<MyFavouritesViewService>
#namespace Server
<h3>Favourite Number</h3>
<div class="p-5">
<select class="form-select" #onchange="NumberChanged">
#foreach (var option in options)
{
if (option.Key == this.Service.Favourites.FavouriteNumber)
{
<option selected value="#option.Key">#option.Value</option>
}
else
{
<option value="#option.Key">#option.Value</option>
}
}
</select>
<div>
<button class="btn btn-success" #onclick="SaveFavourites">Save</button>
</div>
</div>
<MyFavouriteNumber Favourites=this.Service.Favourites />
#code {
private Dictionary<int, string> options = new Dictionary<int, string>
{
{0, "Zero"},
{1, "One"},
{2, "Two"},
{3, "Three"},
};
// Use OnInitializedAsync to get the original values from the data store
protected async override Task OnInitializedAsync()
=> await this.Service.GetFavourites();
// Demo to show saving
private async Task SaveFavourites()
=> await this.Service.SaveFavourites();
// Async setup ensures GetNewNumberInfo runs to completion
// before StatehasChanged is called by the Handler
// Renderer the checks what's changed and calls SetParamaterAsync
// on MyFavouriteNumber because FavouriteNumber has changed
private async Task NumberChanged(ChangeEventArgs e)
{
if (int.TryParse(e.Value?.ToString(), out int value))
await this.Service.GetNewNumberInfo(value);
}
}
I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});
I'm using OData v5/Web API 2.2 to create an endpoint that will return a list of employees from each company.
My problem occurs when I try to implement server-side paging while also using the OData $expand property. When I try to make a call to
http://localhost:60067/Companies?$expand=Employees
I get an error that says "Could not find a property named 'Employees' on type 'System.Web.OData.Query.Expressions.SelectAllAndExpand_1OfCompanyApiModel'"
However, when I removed the EnableQuery attribute the call to the endpoint or when I didn't expand it works as expected. Does anyone have an idea of what I am doing wrong? I've been googling this for a while but haven't found anything.
Here are some code snippets -
Data Models:
public class CompanyApiModel
{
[Key]
public Guid CompanyGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
public List<EmployeeApiModel> Employees { get; set; }
}
public class EmployeeApiModel
{
[Key]
public Guid EmployeeGuid { get; set; }
[Required]
public string Name { get; set; }
// other properties
}
CompaniesController.cs:
[EnableQuery(PageSize = 10)] // If I comment this out everything works
//[EnableQuery] // This fails as well
public IHttpActionResult Get(ODataQueryOptions<CompanyApiModel> queryOptions)
{
var companies = GetCompanies(queryOptions);
return Ok(companies);
// return Ok(companies.AsQueryable()); // This doesn't work either
}
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var routingConventions = ODataRoutingConventions.CreateDefault();
routingConventions.Insert(0, new OptionsRoutingConvention());
config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
// below code allows endpoints to respond with either XML or JSON, depending on accept header preferences sent from client
// (default in absence of accept header is JSON)
var odataFormatters = ODataMediaTypeFormatters.Create();
config.Formatters.InsertRange(0, odataFormatters);
config.EnsureInitialized();
}
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "Demos";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<CompanyApiModel>("Companies");
builder.EntitySet<EmployeeApiModel>("Employees");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
Figured out the problem. We were overriding the EnableQuery attribute somewhere in our code and calling it EnableMappedQuery and applying it to the controller. Thus instead of having [EnableQuery(PageSize = 10)] I should have had [EnableMappedQuery(PageSize = 10)].
EnableQuery Attribute do many works,
1. it will validate the queryoption for you.
2. it will apply the queryoption for you.
3. it can add some querysettings like PageSize.
Your scenario not working is because your GetCompanies is already applied the queryoption, so when EnableQuery get the result and apply the queryoption again, it fails, it can't find the expand property, my suggestion is just return original Company and let EnableQuery do the reset of work for you, ODataQueryOption in parameter is also not needed.
If you realy do some custom work in GetCompanies and don't need EnableQuery to apply for you, you can add PageSize in ODataQuerySettings when you call method ODataQueryOptions.ApplyTo(IQueryable, ODataQuerySettings).
How to bind nullable bool to checkbox in MVC 2. I try with this code in view:
<%: Html.CheckBoxFor(model => model.Communication.Before)%>
But show me compilation error.
Thanks in advance.
I know about this issue. You can try to use this workaround:
Create new property called Before in yours ViewModel:
public class YoursViewModel
{
public Communication Communication { get; set; }
public bool Before
{
get
{
bool result;
if (this.Communication.Before.HasValue)
{
result = (bool)this.Communication.Before.Value;
}
else
{
result = false;
}
return result;
}
set
{
this.Communication.Before = value;
}
}
}
Also you have to be careful for Communication property this have to be instanced before use. For example when you initialize ViewModel in controller you also have to initialize this property.
ControllerAction()
{
YoursViewModel model = ViewModelFactory.CreateViewModel<YoursViewModel >("");
model.Communication = new Communication ();
return View(model);
}
Thanks
Ivan Baev
A checkbox can have two states: ckecked/uncheked, true/false, 1/0. So trying to bind a checkbox to a property that could potentially have three states doesn't really fit the picture. I would recommend you adapting your view model so that it uses a non nullable boolean property. If in your domain model you have a nullable boolean which you cannot change you could do this in the mapping layer between your domain model and view model.
One way to bind Checkbox in MVC View
With EF database first, boolean (bit) field in the database produces a nullable bool? Property in the generated class. For demo I have a table named Dude with the fields
Id uniqueidentifier
Name varchar(50)
IsAwesome bit
The following class is generated by EF:
namespace NullableEfDemo
{
using System;
public partial class Dude
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<bool> IsAwesome { get; set; }
}
}
To be able to bind IsAwesome to checkbox I simply extend the class Dude. This is to avoid editing the generated class, if I need to refresh it. So I added a code file DudePartial.cs to my project (the name is irrelevant). Don’t forget to declare or using the project namespace:
namespace NullableEfDemo
{
partial class Dude
{
public bool Awesome
{
get { return IsAwesome ?? false; }
set { IsAwesome = value; }
}
}
}
This declares a new property Awesome of type bool that can be bound to the checkbox in the Edit view
#Html.CheckBoxFor(model => model.Awesome, new { #class = "control-label" })
In the HttpPost I’m binding the models Awesome property instead of IsAwesome.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,Awesome")] Dude dude)
{…