Blazor form validation to readonly fields - forms

I am developing an application using C# in Blazor Framework. I have designed some forms like the following, where the grey areas are populated with the button below which triggers a pop up window for selection. Then after selection is done the selected item description will populated into the gray area. This grey area is an InputText element.
If the grey InputTexts are marked as required & readonly, then the areas are grey and users cannot insert manually their values but only though selection window. This is good, but if the user did not populate the window for selection it can also submit the form.
If the grey InputTexts are marked as required and beeing readonly though css, then the validation works, so the user should populate the window selection first, but if he did not, then the grey area becomes editable for manual input.
Any ideas how I can protect the application from manual input but at the same time make the validation work?

Any ideas how I can protect the application from manual input but at the same time make the validation work?
If I'm reading the question correctly, the demo below shows how to link the selector (in this case a select control) and the display and show the correct validation information and UX without access to the readonly control.
As you show no code, I don't know whether this fits with your model and form.
#page "/"
#using System.ComponentModel.DataAnnotations;
<PageTitle>Index</PageTitle>
<h1>Demo</h1>
<EditForm Model="model" OnValidSubmit=#OnSubmit class="border border-dark p-3 m-2">
<DataAnnotationsValidator />
<div class="mb-2">
<label class="form-label">Country</label>
<InputText class="form-control" disabled #bind-Value="#model.Value" />
<ValidationMessage For="() => model.Value" />
</div>
#if (!show)
{
<div class="mb-2">
<button type="button" class="btn btn-dark" #onclick=OnShow>Select Country</button>
</div>
}
else
{
<div class="mb-2">
<InputSelect class="form-select" #bind-Value:get=#model.Value #bind-Value:set="this.OnSetCountry">
<option value="">-- Select a Country -- </option>
<option value="UK">UK</option>
<option value="France">France</option>
<option value="Portugal">Portugal</option>
</InputSelect>
</div>
}
<div class="col=12 mt-2 text-end">
<button class="btn btn-success" type="submit">Submit</button>
</div>
</EditForm>
<h3 class="mt-4">Hides the -- Select a Country -- once a value is selected</h3>
<EditForm Model="model2" OnValidSubmit=#OnSubmit class="border border-dark p-3 m-2">
<DataAnnotationsValidator />
<div class="mb-2">
<label class="form-label">Country</label>
<InputText class="form-control" disabled #bind-Value="#model2.Value" />
<ValidationMessage For="() => model2.Value" />
</div>
#if (!show2)
{
<div class="mb-2">
<button type="button" class="btn btn-dark" #onclick=OnShow2>Select Country</button>
</div>
}
else
{
<div class="mb-2">
<InputSelect class="form-select" #bind-Value:get=#model2.Value #bind-Value:set="this.OnSetCountry2">
#if (model2.Value is null)
{
<option selected disabled value="">-- Select a Country -- </option>
}
<option value="UK">UK</option>
<option value="France">France</option>
<option value="Portugal">Portugal</option>
</InputSelect>
</div>
}
<div class="col=12 mt-2 text-end">
<button class="btn btn-success" type="submit">Submit</button>
</div>
</EditForm>
#code {
private Model model = new();
private bool show = false;
private Model model2 = new();
private bool show2 = false;
private void OnSetCountry(string? value)
{
model.Value = null;
if (value is not null || value != string.Empty)
model.Value = value;
show = false;
}
private void OnSetCountry2(string? value)
{
model2.Value = null;
if (value is not null || value != string.Empty)
model2.Value = value;
show2 = false;
}
private void OnShow()
=> show = !show;
private void OnShow2()
=> show2 = !show2;
public void OnSubmit()
{ }
public class Model
{
[Required]
public string? Value { get; set; }
}
}

Related

How to set default dropdown value from in Blazor

recently i am playing around with blazor-pizza shops tutorial.
I think of something to fix here.
Current:
American bacon is my first selection, after i selected it, the dropdownlist is selecting Artichoke hearts
Outcome I want:
Dropdownlist to reset default selecting "(select)" after i select the American bacon
My code like this:
#inject HttpClient HttpClient
#code {
List<Topping> toppings;
[Parameter] public Pizza Pizza { get; set; }
[Parameter] public EventCallback OnCancel { get; set; }
[Parameter] public EventCallback OnConfirm { get; set; }
protected async override Task OnInitializedAsync()
{
toppings = await HttpClient.GetFromJsonAsync<List<Topping>>("toppings");
}
void ToppingSelected(ChangeEventArgs e)
{
if (int.TryParse((string)e.Value, out var index) && index >= 0)
{
AddTopping(toppings[index]);
toppings.Remove(toppings[index]);
}
}
void AddTopping(Topping topping)
{
if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null)
{
Pizza.Toppings.Add(new PizzaTopping() { Topping = topping });
}
}
void RemoveTopping(Topping topping)
{
Pizza.Toppings.RemoveAll(pt => pt.Topping == topping);
toppings.Add(topping);
toppings = toppings.OrderBy(p=>p.Name).ToList();
}
}
<div class="dialog-container">
<div class="dialog">
<div class="dialog-title">
<h2>#Pizza.Special.Name</h2>
#Pizza.Special.Description
</div>
<form class="dialog-body">
<div>
<label>Size:</label>
<input type="range" min="#Pizza.MinimumSize" max="#Pizza.MaximumSize" step="1" #bind="Pizza.Size" #bind:event="oninput" />
<span class="size-label">
#(Pizza.Size)" (£#(Pizza.GetFormattedTotalPrice()))
</span>
</div>
<div>
<label>Extra Toppings:</label>
#if (toppings == null)
{
<select class="custom-select" disabled>
<option>(loading...)</option>
</select>
}
else if (Pizza.Toppings.Count >= 6)
{
<div>(maximum reached)</div>
}
else
{
<select id="ToppingSelection" class="custom-select" #onchange="ToppingSelected">
<option value="-1" disabled selected>(select)</option>
#for (var i = 0; i < toppings.Count; i++)
{
<option value="#i">#toppings[i].Name - (£#(toppings[i].GetFormattedPrice()))</option>
}
</select>
}
</div>
<div class="toppings">
#foreach (var topping in Pizza.Toppings)
{
<div class="topping">
#topping.Topping.Name
<span class="topping-price">#topping.Topping.GetFormattedPrice()</span>
<button type="button" class="delete-topping" #onclick="#(() => RemoveTopping(topping.Topping))">x</button>
</div>
}
</div>
</form>
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto" #onclick="OnCancel">Cancel</button>
<span class="mr-center">
Price: <span class="price">#(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto" #onclick="OnConfirm">Order ></button>
</div>
</div>
I would recommend rewriting the form to use the built-in Blazor components like InputSelect with proper two-way binding etc.
However, a "quick" fix could be something like a static binding. First, create a field that always has a value of -1 which is your code for (select)
#code {
private readonly Int32 _toppingsIndex = -1;
}
In the next step, set the value to the select control.
<select id="ToppingSelection" class="custom-select"
#onchange="ToppingSelected"
value="#_toppingsIndex">
<option value="-1" disabled>(select)</option>
#for (var i = 0; i < toppings.Count; i++)
{
<option value="#i">#toppings[i].Name - (£#(toppings[i].GetFormattedPrice()))</option>
}
</select>
Explanation
As soon as you select something, your method ToppingSelected is executed, and the topping is removed from the list. As soon as the method has finished, a new render cycle is started. The value is still -1, so the option with the value -1 will be selected after the rendering is finished.

How to submit a form multiple times (asp.net core razor)

I am trying to create a form that can be submitted multiple times with different information, while retaining a common value in one field.
I have a list view from a SQL table in ASP.NET Core Razor that is a list of construction projects. For each row in the list I have a link that goes to a "create" template page where users can create a bid entry for the project which is stored in a different table. The Project Number is assigned to a route value (asp-route-Number = "the project number from the previous list")and populates a hidden field in the "create new bid" form.
Using the default code for the razor page, everything works great. You click submit and are taken back to the list of projects.
What I want to do is have another option on the "create new bid" form that will allow you to save and enter another bid for the same project. I created another button and handler to do this but I am stuck on actually implementing it. If I use return Page() the form posts and the page is returned with route data intact, but the text fields still contain the previous data and the drop-down list is empty. If I use return RedirectToPage(CreateNewBid, Route data) the form posts but the route data does not seem to be passed along and creates a null value error.
This is the link from the Projects list (inside the foreach table), which takes you to the "Create Bid" form and works fine.
<a asp-page="CreateBid" asp-route-Number="#item.ProjectNumber" asp-route-opwid="#item.Id">New Bid</a>
The Create Bid form has the following to submit and create another entry
int num = int.Parse(Request.Query["Number"]);
int idnum = int.Parse(Request.Query["opwid"]);
<input type="submit" value="Save and enter another"
asp-page-handler="Another" asp-route-opwid="#idnum"
asp-route-Number="#num" class="btn btn-primary"/>
And the handler:
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return Page();
//return RedirectToPage("./CreateBid", (Number == num, opwid == idnum));
}
I have also tried several things in the route parameters (as opposed to using the variables) in the "Redirect to Page" and nothing seems to work.
Is there an easier way, or am I just missing something?
This is the cshtml file:
#page
#model Authorization_AD.Pages.GenSvc.BidEntry.CreateBidModel
#{
ViewData["Title"] = "CreateBid";
}
#{ int num = int.Parse(Request.Query["Number"]);
int idnum = int.Parse(Request.Query["opwid"]);
}
<h1>Create Bid</h1>
<h4>OPW number #num</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="OpwBids.OpwProject" value="#idnum" hidden class="form-control" />
</div>
<div class="form-group">
<label asp-for="OpwBids.OpeningDate" class="control-label"></label>
<input asp-for="OpwBids.OpeningDate" class="form-control" />
<span asp-validation-for="OpwBids.OpeningDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OpwBids.Contractor" class="control-label"></label>
<select asp-for="OpwBids.Contractor" class="form-control" asp-items="ViewBag.Contractor">
<option disabled selected>--- SELECT ---</option>
</select>
</div>
<div class="form-group">
<label asp-for="OpwBids.BidAmount" class="control-label"></label>
<input asp-for="OpwBids.BidAmount" class="form-control" />
<span asp-validation-for="OpwBids.BidAmount" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save and enter another"
asp-page-handler="Another" asp-route-opwid="#idnum"
asp-route-Number="#num" class="btn btn-primary"/>
<input type="submit" value="Save and return to list" asp-page-handler="Done" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
This is the C# file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Authorization_AD.Models;
namespace Authorization_AD.Pages.GenSvc.BidEntry
{
public class CreateBidModel : PageModel
{
private readonly Authorization_AD.Models.OPWContext _context;
public CreateBidModel(Authorization_AD.Models.OPWContext context)
{
_context = context;
}
public IActionResult OnGet()
{
ViewData["Contractor"] = new SelectList(_context.Contractors, "Id", "ContractorName");
ViewData["OpwProject"] = new SelectList(_context.MainProjectsListing, "Id", "ProjectNumber");
return Page();
}
[BindProperty]
public OpwBids OpwBids { get; set; }
public async Task<IActionResult> OnPostDoneAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return Page();
//return RedirectToPage("./CreateBid", (Number == OpwBids.OpwProjectNavigation.ProjectNumber, opwid == OpwBids.OpwProject));
}
}
}
You can add a property to your page that will be used to bind the value of the clicked button.
public class CreateBidModel : PageModel {
//...
//Add this property to your page.
[BindProperty]
public string Button {get;set;}
public void OnGet(int number,string opwid){
//Set the number and opwid to the target properties
}
public Task<IActionResult> OnPostAsync(){
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
if(Button == "finish"){
return RedirectToPage("./Index");
}
else {
return RedirectToPage("./CreateBid", (Number == OpwBids.OpwProjectNavigation.ProjectNumber, opwid == OpwBids.OpwProject));
}
}
}
To the view you need to add two buttons that have the same name and that value will be mapped to the Button property.
<form method="post">
... Other content goes here
<button name="#Html.NameFor(m => m.Button)" value="another">Create another</button>
<button name="#Html.NameFor(m => m.Button)" value="finish">Finish</button>
</form>
The value of the clicked button will be parsed to the Button property of the Pagemodel. Based on the value you can decide how to further handle the response of the request (Finish / Create another one in your case).
Thanks for everyone's help. I got it to do what I want by adding the following to the "OnPostAnotherAsync" task:
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
ViewData["Contractor"] = new SelectList(_context.Contractors, "Id", "ContractorName");
ModelState.SetModelValue("OpwBids.BidAmount", new ValueProviderResult(string.Empty, CultureInfo.InvariantCulture));
ModelState.SetModelValue("OpwBids.Contractor", new ValueProviderResult(string.Empty, CultureInfo.InvariantCulture));
return Page();
}
After the "Save Changes" I needed to re-load the view data for the "Contractor" drop down list. Then it was just a matter of clearing the form fields before returning the page.

How to implement validation rules for elements from a partial view

We are developing a .net core 3.1 MVC application (actual with MVVMC).
We have implemented custom validation attributes. And want them to be checked on server and on client side.
The view is a standard view, however the user has the possibility to add multiple partial views to the standard view (via a button).
In the partial view we were not able to use the 'asp-for' tag helper within the input fields, because one can have several times the same item added, and we need to be able to create a list in the ViewModel out of those additional partial views selected. That's why we tried to build the tags by ourselfes.
Once a partial view has been requested via ajax we are calling the validator again. Server validation works, client validation only works for those inputs, which are on the main view, not for the inputs on the partial views. However, after refreshing or when the server sends the user back after a validation error, client validation starts working (because site is completely refreshed and jquery validation takes input fields of partial views into account).
Please find the code below:
Implementation of validation attribute
public class AllowedExtensionsAttribute: ValidationAttribute, IClientModelValidator
{
private readonly List<string> _extensions = new List<string>();
public AllowedExtensionsAttribute(string extensions)
{
_extensions.Add(extensions);
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is IFormFile file)
{
var extension = Path.GetExtension(file.FileName);
if (!_extensions.Contains(extension.ToLower()))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-extension", ErrorMessage);
}
}
MainViewModel
[AllowedExtensions(".pdf", ErrorMessage = "This data type is not allowed!")]
public IFormFile PdfDocumentOne { get; set; }
PartialViewModel
[AllowedExtensions(".pdf", ErrorMessage = "This data type is not allowed!")]
public IFormFile PdfDocumentTwo { get; set; }
Main View
<form method="post" asp-controller="Home" asp-action="MainView" enctype="multipart/form-data">
<div class="form-group top-buffer">
<div class="row">
<div class="col-2">
<label asp-for="PdfDocumentOne" class="control-label"></label>
</div>
<div class="col-3">
<input asp-for="PdfDocumentOne" class="form-control-file" accept="application/pdf" />
<span asp-validation-for="PdfDocumentOne" class="text-danger"></span>
</div>
</div>
</div>
<div id="container">
<div id="containerFull" class="form-group">
<div class="row">
<div class="col-2">
<label class="control-label">...</label>
</div>
<div class="col-10">
<div id="containerPartialView">
</div>
</div>
</div>
<div class="row">
<div class="col-2">
</div>
<div class="col-3">
<button id="AddPartialView" type="button" class="form-control">...</button>
</div>
</div>
</div>
</div>
...
<div class="form-group top-buffer">
<div class="row">
<div class="col-2">
<input type="submit" value="Submit" class="form-control" id="checkBtn" />
</div>
</div>
</div>
</form>
Partial View
<input id="Lists[#Model.ViewId].ViewId" name="Lists[#Model.ViewId].ViewId" class="partialViewModel" type="hidden" value="#Model.ViewId" />
<div id="Lists[#Model.ViewId].MainContainer" class="partialView">
<div class="form-group">
<div>
<div class="col-2">
<label asp-for="PdfDocumentTwo" class="control-label"></label>
</div>
<div class="col-3">
<input name="Lists[#Model.ViewId].PdfDocumentTwo" id="Lists[#Model.ViewId].PdfDocumentTwo " type="file" class="form-control-file" accept="application/pdf"
data-val="true" data-val-extension="This data type is not allowed!"/>
<span class="text-danger field-validation-valid" data-valmsg-for="Lists[#Model.ViewId].PdfDocumentTwo" data-valmsg-replace="true"></span>
</div>
</div>
</div>
...
</div>
Javascript
function AddPartialView() {
var i = $(".partialView").length;
$.ajax({
url: '/Home/AddPartialView?index=' + i,
success: function (data) {
$('#containerPartialView').append(data);
$().rules('remove','extension');
jQuery.validator.unobtrusive.adapters.addBool("extension");
},
error: function (a, b, c) {
console.log(a, b, c);
}
});
}
$('#AddPartialView').click(function () {
AddPartialView();
});
jQuery.validator.addMethod("extension",
function (value, element, param) {
var extension = value.split('.').pop().toLowerCase();
if ($.inArray(extension, ['pdf']) == -1) {
return false;
}
return true;
});
jQuery.validator.unobtrusive.adapters.addBool("extension");
HomeController
[HttpPost]
public IActionResult MainView(MainViewModel vm)
{
if (vm == null)
{
return RedirectToAction("Index");
}
DatabaseHelper.GetMainViewModel(vm);
if (!ModelState.IsValid)
{
#ViewData["StatusMessageNegative"] = "The entered data is not valid. Please scroll down to correct your data.";
return View(vm);
}
return RedirectToAction("UploadDocument", new { Id = vm.Id});
}
[HttpGet]
public ActionResult AddPartialView(int index)
{
PartialViewModel pvm = DatabaseHelper.GetPartialViewModel(index);
return PartialView("Partial", pvm);
}
After searching in the internet we found the following argument (data-ajax="true"). However, we don't know where to place it or how to use it correctly.
Have you tried re-apply validation after loading your partial view?
$.ajax({
url: '/Home/AddPartialView?index=' + i,
success: function (data) {
$('#containerPartialView').append(data);
$().rules('remove','extension');
jQuery.validator.unobtrusive.adapters.addBool("extension");
},
error: function (a, b, c) {
console.log(a, b, c);
}
}).then(function () {
$("form").each(function () { $.data($(this)[0], 'validator', false); });
$.validator.unobtrusive.parse("form");
});

Getting CheckList data in MVC6 post

I am doing user roles in MVC6 (using EF7), and I am think I am just missing something, but on a form used to define a user roles I get nothing in the posted back Model to the controller
This is my Model
public class UserRoleItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IdentityUserRole<int> userRole { get; set; }
public bool HasRole { get; set; }
}
public class UsersRolesViewModel
{
public int UserId { get; set; }
public string FullName { get; internal set; }
public List<UserRoleItem> UserRoles;
}
This is the view
#model Skill.ViewModels.Manage.UsersRolesViewModel
<br />
<h3>Set Roles for user - #Model.FullName</h3>
<form asp-action="ChangeUsersRoles" >
<div class="form-horizontal">
<hr />
<input type="hidden" asp-for="UserId" />
#foreach (var userRole in Model.UserRoles)
{
<input type="hidden" asp-for="#userRole.Id" />
<div class="form-group">
<div class="inline-block col-md-8">
<span class="col-md-1" align="center">
<input type="checkbox" asp-for="#userRole.HasRole" />
</span>
<div class="col-md-7">
#userRole.Name (#userRole.Description)
</div>
</div>
</div>
}
<hr />
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
This is the Controller
// GET: Users/ChangeUserRoles/5
public async Task<IActionResult> ChangeUserRoles(int? id)
{
if (id == null)
{
return HttpNotFound();
}
var model = new UsersRolesViewModel();
ApplicationUser appUser = await _context.ApplicationUsers.SingleAsync(m => m.Id == id);
if (appUser == null)
{
return HttpNotFound();
}
else
{
model.UserId = (int)id;
model.FullName = appUser.FullName;
var some = from r in _context.Roles
from ur in _context.UserRoles
.Where(inner => r.Id == inner.RoleId && inner.UserId == id)
.DefaultIfEmpty()
select new UserRoleItem
{
Id = (int)r.Id,
Name = r.Name,
Description = r.NormalizedName,
userRole = ur, // this is needed else it has a hissy fit
HasRole = (ur != null)
};
// get all of the Roles and then also link to the ones the user currently has
model.UserRoles = (some).ToList();
}
return View(model);
}
// POST: Users/ChangeUserRoles/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangeUserRoles([Bind(include:"UserId,UserRoles")]UsersRolesViewModel userModel)
{
if (ModelState.IsValid)
{
// update based on the changes
// return RedirectToAction("Edit", new { userModel.UserId });
}
return View(userModel);
}
So when I get the post back on Save, the UserRoles list is null, so I assume I am just missing an obvious thing here?
Also another small issue is the EF Linq statement. If I remove the
userRole = ur,
statement from the Select portion of the Linq query, the system has a fit and says my schema is out of date (which it isn't). I think it is due to the following statement where I am testing the outer join value
HasRole = (ur != null)
Although this seems perfectly reasonable and works if the ur variable is used prior to testing for null (or not)
After entering this question - I further investigated the issue and found that I could do what I needed using an EditorTemplate
So I created the EditorTemplate Folder under my Users View folder as it was a UsersController and then added the following UserRoleItem.cshtml file
#model UserRoleItem
<input type="hidden" asp-for="Id" />
<div class="form-group">
<div class="inline-block col-md-8">
<span class="col-md-1" align="center">
<input type="checkbox" asp-for="HasRole" />
</span>
<div class="col-md-7">
#Model.Name (#Model.Description)
</div>
</div>
</div>
I then changed the view (called ChangeUsersRole.cshtml) to be
#model Skill.ViewModels.Manage.UsersRolesViewModel
<br />
<h3>Set Roles for user - #Model.FullName</h3>
<hr />
<form asp-action="ChangeUsersRoles">
<div class="form-horizontal">
<input type="hidden" asp-for="UserId" />
#Html.EditorFor(m => m.UserRoles)
<hr />
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
Note also: that I tried using the new format
<input asp-for="UserRoles" />
instead of this line in the view
#Html.EditorFor(m => m.UserRoles)
But it did not work as expected. Again maybe I am still doing something wrong - or maybe that feature isn't working yet?

Post complex model to controller MVC using Facebook application template C#

I have following model:
class HomePageModel
{
public User user { get; set; }
public ResultHistory resultHistory { get; set; }
public Option option { get; set; }
public HomePageModel()
{
}
}
Code from controller when passing it into view:
[FacebookAuthorize]
public async Task<ActionResult> Index(Context context)
{
ViewBag.AppUrl = GlobalFacebookConfiguration.Configuration.AppUrl;
if (ModelState.IsValid)
{
var user = await context.Client.GetCurrentUserAsync<MyAppUser>();
var option = new Option();
//CODE for reading user from db, simpified
Users dbUser = db.Get(context);
var resultHistory = new ResultHistory(dbUser.NSPGW, dbUser.NSPGL,
dbUser.NMPGW,dbUser.NMPGL,dbUser.NDPGW, dbUser.NDPGL);
HomepageModel homepageModel = new HomepageModel();
homepageModel.option = option;
homepageModel.resultHistory = resultHistory;
homepageModel.user = user;
return View(homepageModel);
}
}
Code from controller
#using (Html.BeginForm("Start", "Home", FormMethod.Post, new { id="opcijeForm"}))
{
<div class="row">
<div class="col-md-4">
<div class="radio">
<label>
<input type="radio" name="radio" value="sp" />SP
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio" value="mp" />MP
</label>
</div>
<div>
<input type="submit" value="start" />
</div>
</div>
<div class="col-md-8">
<div id="spoption">
#Html.DropDownListFor(m => m.option.sl, Model.option.slSelectList)
<label style="padding-left:10px">
<input type="checkbox" id="bb" /> BB
</label>
#Html.DropDownListFor(m => m.option.swt, Model.option.swtSelectList, new {id="bbdd" })
<label style="padding-left:10px">
<input type="checkbox" id="ziv" /> ziv
</label>
#* #Html.DropDownListFor(m => m.gameOption.sln, Model.option.slnSelectList, new { id="zivdd"})*#
</div>
<div id="mpoption">
<label>Numer of oppointment</label>
#Html.DropDownListFor(m=>m.option.spn, Model.option.spnSelectList)
#Html.DropDownListFor(m=>m.option.smv, Model.option.smvSelectList)
</div>
</div>
</div>
}
and receiving model on post
[FacebookAuthorize]
[HttpPost]
public ActionResult Start(HomepageModel model)
{
//Some code
//here is for example model.option null
}
When I submit form with above model, in my controller action where I receive same model, all it's properties are null.Each class that is within HomePage has only string and int as properties. If I move something, for example from class Option and put it in HomePageModel direct, then properties I have moved are not null. Is there a way to post model as above, or should I just copy properties from these three class into HomePageModel class?
[EDIT]
It is because of meta-data FacebookAuthorize, when I remove it everything is working
MVC app facebook passing object from view to controller at submit