Razor Page Partial renders Input and hidden Input - c#

I´m experiencing a very strange behaviour with ASP .net core 2.2 Razor Pages.
Let´s say I have a standard Razor Page. The User can send a request over that page for a new Team that should be provisioned. I have some fields like, DisplayName etc. which the user can edit freely and a dropdown to choose from where he can select from which existing Team he would like to clone some settings from. After the User selected it, I´m using Ajax to render the partial View on the right side of the view and the User can also select some additional options. Until here I´m fine and everything is working so far. I expected, if the user clicks the save / submit button that everything will be included in the post request, even the partial View Data. After digging around and troubleshooting I found out that I have to set ViewData.TemplateInfo.HtmlFieldPrefix to name everything like in the Parent View to not mess the Binding up. Works also.
The strange behaviour is, that after the Partial View was rendered, in HTML two Inputs fields were generated. One where the user can select and one which is hidden. I´ll show my code and also the rendered HTML File so that guys can make yourself a picture of that. What now happens is, the hidden fields have a default or null values and these are going to be included in the request. I have no idea where they come from and / or how to fix it or what I did wrong?
I will shorten my code a little bit but I will include the necessary parts.
Team Request Model
public class TeamRequestModel
{
public string DisplayName { get; set; }
public string Description { get; set; }
public string Visibility { get; set; }
public List<string> Owners { get; set; }
public string CloneTeamID { get; set; }
public TeamCloneSettings TeamCloneSettings { get; set; }
public TeamRequestModel()
{
TeamCloneSettings = new TeamCloneSettings();
}
}
Team Clone Settings (only Parts can be Set by the user
public class TeamCloneSettings
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public string PartitionKey;
public string DisplayName { get; set; }
public bool Released { get; set; }
public bool FixedVisibility { get; set; }
public string Visibility { get; set; }
public bool CloneEverything { get; set; }
public TeamCloneParts Parts { get; set; }
public TeamCloneSettings()
{
Parts = new TeamCloneParts();
}
}
public class TeamCloneParts
{
public bool CloneApps { get; set; }
public bool CloneChannels { get; set; }
public bool CloneTabs { get; set; }
public bool CloneSettings { get; set; }
}
}
Main "Team Request View"
#page
#model iwDashboard.Pages.Teams.TeamRequest
#{
ViewData["Title"] = Model.Title;
}
#{
ViewBag.PageTitle = Model.Title;
}
<style>
textarea {
resize: none;
}
</style>
<section class="content">
<form method="post" asp-page-handler="SaveTeamRequest" class="col-md-12">
<div class="row">
<div class="col-md-3">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Team Details</h3>
</div>
<div class="card-body">
<div class="form-group">
<label for="displayName">Display Name</label>
<input id="displayName" asp-for="#Model.teamRequest.DisplayName" type="text" class="form-control" required />
</div>
... More fields
<div style="float: left; width: 40%">
<button type="button" onclick="history.go(-1)" data-toggle="tooltip" title="Back" class="btn btn-primary btn-block btn-sm"><i class="fa fa-arrow-circle-left"></i><b> Back</b></button>
</div>
<div style="float: right; width: 40%">
<button type="submit" data-toggle="tooltip" title="Save" class="btn btn-success btn-block btn-sm"><i class="fas fa-save"></i><b> Save</b></button>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<div class="col-md-6">
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Team Settings</h3>
</div>
<div class="card-body">
<!--partial comes here-->
<div id="partialDiv"></div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
</div>
</form>
</section>
#section Scripts
{
<script>
function GetTeamTemplateProperties() {
var selectedTeam = $('#teamTemplateSelection').val();
$.ajax({
type: "Get",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
url: '/Teams/TeamRequest?handler=TeamTemplateProperties',
data: {
SelectedTeam: selectedTeam
},
success: function (result) {
$("#partialDiv").after(result);
}
})
};
</script>
}
Get Template Properties Post called by Ajax
public async Task<IActionResult> OnGetTeamTemplateProperties(string SelectedTeam)
{
TeamCloneSettings teamCloneSettings = new TeamCloneSettings();
teamRequest = new TeamRequestModel();
if (!string.IsNullOrEmpty(SelectedTeam))
{
teamCloneSettings = await _teamCloneSettingService.GetTeamCloneSettingByIdAsync(SelectedTeam);
teamRequest.TeamCloneSettings = teamCloneSettings;
}
ViewData.TemplateInfo.HtmlFieldPrefix = "teamRequest";
SelectedValue = SelectedTeam;
return new PartialViewResult
{
ViewName = "_TeamCloneSettingsPartial",
ViewData = new ViewDataDictionary<TeamRequestModel>(ViewData, teamRequest)
};
}
View Model BindProperty
...More Code
[BindProperty]
public TeamRequestModel teamRequest { get; set; }
[BindProperty]
public string SelectedValue { get; set; }
...More Code
_TeamCloneSettingsPartial
#model iwDashboard.Models.TeamRequestModel
#{
<!--
Check if fixed visibility is enabled and
disable selection of visibility if it is
-->
string disabled = null;
string options = null;
if (Model.TeamCloneSettings.FixedVisibility)
{
disabled = "disabled";
options = Model.Visibility;
}
}
<div class="form-group">
<input type="checkbox" hidden id="cloneEverthing" checked=#Model.TeamCloneSettings.CloneEverything>
<ul class="list-group list-group-unbordered mb-3">
<li class="list-group-item">
<label for="visibility">Visibility</label>
<select class="form-control #disabled custom-select" asp-for="TeamCloneSettings.Visibility" required>
#if (!string.IsNullOrEmpty(disabled))
{
<option readonly selected>#Model.TeamCloneSettings.Visibility</option>
}
else
{
<option>Public</option>
<option>Private</option>
}
</select>
</li>
<!-- Checkbox parts here
Javascript will check if "CloneEverything"
is enabled and will disable all of the following checkboxes
-->
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneApps" checked=#Model.TeamCloneSettings.Parts.CloneApps asp-for="TeamCloneSettings.Parts.CloneApps">
<label class="custom-control-label" for="cloneApps">Clone Apps</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneChannels" checked=#Model.TeamCloneSettings.Parts.CloneChannels asp-for="TeamCloneSettings.Parts.CloneChannels">
<label class="custom-control-label" for="cloneChannels">Clone Channels</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneSettings" checked=#Model.TeamCloneSettings.Parts.CloneSettings asp-for="TeamCloneSettings.Parts.CloneSettings">
<label class="custom-control-label" for="cloneSettings">Clone Settings</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneTabs" checked=#Model.TeamCloneSettings.Parts.CloneTabs asp-for="TeamCloneSettings.Parts.CloneTabs">
<label class="custom-control-label" for="cloneTabs">Clone Tabs</label>
</div>
</div>
</li>
</ul>
</div>
Strange rendered Input field
https://i.stack.imgur.com/aZtUD.png
As you can see, the Input is rendered twice and only the hidden values will be transmitted. But this only happens for the Input fields in the Partial View, all other fields are fine.
Any Ideas? Would be great :-).
Thanks a lot!!

Having 2 input fields generated is normal when using the asp-for tag helper on a checkbox. It will create the checkbox itself but also a hidden input field for the model binder to send the value chosen by the user.
Only the hidden field generated by the tag helper will be used to send the data.

Found a solution here:
asp.net mvc: why is Html.CheckBox generating an additional hidden input
Might not be the best one but it works for me:
checking with Javascript if the Checkbox is selected an then setting the value of the hidden field:
if ($('[name="foo"]:checked').length > 0)
$('[name="foo"]:hidden').val(true);
Thanks everybody!

Related

How to enable validation on MVC form?

I am building a website with MVC and I have a view with a form. I want validation to appear when users leave a field blank, however I can not get this to work. I have tried adding the Data annotation tags in the model, the asp-validation-for span, and checking the ModelState in the post action.
Here is my Model:
using System;
using IssueTracker.Areas.Identity.Data;
using System.ComponentModel.DataAnnotations;
using System.Xml.Linq;
namespace IssueTracker.Models
{
public class EditProjectViewModel
{
public EditProjectViewModel()
{
this.Users = new List<ApplicationUser>();
this.OtherUsers = new List<ApplicationUser>();
}
public int Id { get; set; }
[Required]
public string? Name { get; set; }
[Required]
public string? Description { get; set; }
[Required]
public string? Status { get; set; }
[Required]
public string? ClientCompany { get; set; }
[Required]
public string? ProjectLeader { get; set; }
[Required]
public virtual List<ApplicationUser> Users { get; set; }
[Required]
public virtual List<ApplicationUser> OtherUsers { get; set; }
}
}
and here is my View:
#using IssueTracker.Areas.Identity.Data
#using Microsoft.AspNetCore.Identity
#inject UserManager<ApplicationUser> userManager
#model EditProjectViewModel
#{
ViewData["Title"] = "Edit Project: " + Model.Name;
}
#section Scripts2
{
<link rel="stylesheet" href="../../dist/plugins/select2/css/select2.min.css">
}
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-md-12">
<div class="card card-info">
<div class="card-header">
<h3 class="card-title">Edit Your Project</h3>
</div>
<div class="card-body">
<form method="post">
<div class="form-group">
<label for="inputName">Project Name:</label>
<textarea id="inputName" name="Name" class="form-control" rows="1">#Model.Name</textarea>
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label for="inputDescription">Project Description:</label>
<textarea id="inputDescription" name="Description" class="form-control" rows="3">#Model.Description</textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label for="inputStatus">Status:</label>
<select asp-for="Status" id="inputStatus" name="Status" class="form-control custom-select">
<option selected value="#Model.Status">#Model.Status</option>
#if (Model.Status != "In Development")
{
<option value="In Development">In Development</option>
}
#if (Model.Status != "On Hold")
{
<option value="On Hold">On Hold</option>
}
#if (Model.Status != "Revising")
{
<option value="Revising">Revising</option>
}
#if (Model.Status != "Completed")
{
<option value="Completed">Completed</option>
}
#if (Model.Status != "Withdrawn")
{
<option value="Withdrawn">Withdrawn</option>
}
</select>
<span asp-validation-for="Status" class="text-danger"></span>
</div>
<div class="form-group">
<label for="inputCompany">Client Company:</label>
<textarea asp-for="ClientCompany" id="inputCompany" name="ClientCompany" class="form-control" rows="1">#Model.ClientCompany</textarea>
<span asp-validation-for="ClientCompany" class="text-danger"></span>
</div>
<div class="form-group">
<label for="inputLeader">Project Leader:</label>
<select asp-for="ProjectLeader" id="inputLeader" name="ProjectLeader" class="form-control custom-select">
<option selected value="#Model.ProjectLeader">#(userManager.FindByIdAsync(Model.ProjectLeader).Result.FirstName + " " + userManager.FindByIdAsync(Model.ProjectLeader).Result.LastName)</option>
#foreach (ApplicationUser user in userManager.Users)
{
if (user != userManager.FindByIdAsync(Model.ProjectLeader).Result)
{
<option value="#user.Id">#(user.FirstName + " " + user.LastName)</option>
}
}
</select>
<span asp-validation-for="ProjectLeader" class="text-danger"></span>
</div>
<div class="form-group">
<label>Select 1 or More Contributors:</label>
<div class="select2-blue">
<select asp-for="Users" class="select2" name="Contributors" multiple="multiple" data-dropdown-css-class="select2-blue" data-placeholder="Select a Contributors" style="width: 100%;">
#foreach (ApplicationUser user in Model.Users)
{
<option value="#user.Id" selected>#(user.FirstName + " " + user.LastName)</option>
}
#foreach (ApplicationUser user in Model.OtherUsers)
{
<option value="#user.Id">#(user.FirstName + " " + user.LastName)</option>
}
</select>
</div>
<span asp-validation-for="Users" class="text-danger"></span>
</div>
<div class="row">
<div class="col-12">
<a asp-controller="Home" asp-action="Projects" class="btn btn-secondary">Cancel</a>
<input type="submit" asp-route-id="#Model.Id" value="Update Project" class="btn btn-success float-right">
</div>
</div>
</form>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
</div>
</section>
<!-- /.content -->
<!-- /.content-wrapper -->
#section Scripts{
<script src="../../dist/plugins/select2/js/select2.full.min.js"></script>
<script>$(function () {
$('.select2').select2()
})</script>
}
Lastly, here is my controller method:
[HttpPost]
public async Task<IActionResult> EditProject(int Id, string Name, string Description, string Status, string ClientCompany,
string ProjectLeader, List<string> Contributors)
{
if (ModelState.IsValid)
{
var project = await db.Projects.FindAsync(Id);
if (project == null)
{
return View("Error");
}
else
{
project.Name = Name;
project.Description = Description;
project.Status = Status;
project.ClientCompany = ClientCompany;
project.ProjectLeader = ProjectLeader;
}
db.Entry(project).Collection("Users").Load();
foreach (ApplicationUser user in project.Users)
{
db.Entry(user).Collection("Projects").Load();
user.Projects.Remove(project);
db.Users.Update(user);
}
project.Users.Clear();
foreach (string Contributor in Contributors)
{
project.Users.Add(await userManager.FindByIdAsync(Contributor));
userManager.FindByIdAsync(Contributor).Result.Projects.Add(project);
}
project.Users.Add(userManager.FindByIdAsync(ProjectLeader).Result);
userManager.FindByIdAsync(ProjectLeader).Result.Projects.Add(project);
db.Projects.Update(project);
await db.SaveChangesAsync();
return RedirectToAction("Projects");
}
return RedirectToAction("EditProject", Id);
}
Whenever I submit a form and leave fields blank, the page just refreshes and doesn't save any changes. Instead, I want the error validation messages to pop up. How can I achieve this? Thank you!
For the html validation in View to work with asp.net data validation attributes defined in EditProjectViewModel, you will need javascript/jquery unobtrusive behavior. That will help you generate much of the validation code as well as do the validation for you based on the data attributes defined in the model.
You can take this answer as reference and for help: What is jQuery Unobtrusive Validation?

When i try to get data from data for edit page it's showing me error

When I try to get data from data for edit page it's showing me the following error:
So I am building a website where I want to implement a edit banner form when the User can Edit his banner data.
And here is where the problem I raise, when I run the code, it return always as null.
Here is my controller:
[HttpGet]
public ActionResult EditBanner(int id)
{
var abd = (from a in _db.Banner
where id == a.BannerId
select a).FirstOrDefault();
return View(abd);
}
[HttpPost]
public ActionResult UpdateBannerDatas(BannerViewModels bnrupdate)
{
var bnrdata = _db.Banner.Where(x => x.BannerId == bnrupdate.BannerId).FirstOrDefault();
if (bnrdata != null)
{
bnrdata.BannerTitle = bnrupdate.BannerTitle;
bnrdata.BannerUrl = bnrupdate.BannerUrl;
bnrdata.BannerIndex = bnrupdate.BannerIndex;
bnrdata.BannerDescription = bnrupdate.BannerDescription;
bnrdata.BannerIndex = bnrupdate.BannerIndex;
_db.SaveChanges();
}
return Redirect("BannerDetails");
}
Here is my viewmodel:
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SZGMC.Web.Models;
namespace SZGMC.Web.Areas.Admin.ViewModels
{
public class BannerViewModels
{
public int BannerId { get; set; }
public string BannerTitle { get; set; }
public string BannerDescription { get; set; }
public string BannerImg { get; set; }
public IFormFile BannerImg1 { get; set; }
public string BannerUrl { get; set; }
public string BannerIndex { get; set; }
public int? BMasterId { get; set; }
public byte? IsDeleted { get; set; }
public virtual BannerMaster BMaster { get; set; }
}
}
Here is my model:
using System;
using System.Collections.Generic;
// Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled.
// If you have enabled NRTs for your project, then un-comment the following line:
// #nullable disable
namespace SZGMC.Web.Models
{
public partial class Banner
{
public int BannerId { get; set; }
public string BannerTitle { get; set; }
public string BannerDescription { get; set; }
public string BannerImg { get; set; }
public string BannerUrl { get; set; }
public string BannerIndex { get; set; }
public int? BMasterId { get; set; }
public byte? IsDeleted { get; set; }
public virtual BannerMaster BMaster { get; set; }
}
}
Here is my view:
<form asp-controller="Home" asp-action="UpdateBannerDatas" enctype="multipart/form-data" method="post">
<div id="main-content">
<div class="container-fluid">
<!-- Page header section -->
<div class="block-header">
<div class="row clearfix">
<div class="col-lg-6 col-md-5 col-sm-12">
<h1>Hi, Welcomeback!</h1>
<span>You can edit banner here</span>
</div>
<div class="col-xl-6 col-md-7 col-sm-12 text-md-right">
<div class="d-flex align-items-center justify-content-md-end mt-4 mt-md-0 flex-wrap vivify pullUp delay-550">
<div class="mb-3 mb-xl-0 ">
<a asp-action="BannerDetails" class="btn btn-dark">Banner List</a>
</div>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-12">
<div class="card">
<div class="body">
<div class="header">
<h2><strong>Enter Banner Details</strong></h2>
</div>
<br />
<input asp-for="BannerId" type="hidden" />
<div class="row">
<div class="col-12">
<div class="form-group c_form_group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"></span>
</div>
<input type="text" class="form-control" asp-for="BannerTitle" placeholder="Banner Title" aria-label="bannertitle" aria-describedby="basic-addon1">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group c_form_group">
<label>Banner Description</label>
<div class="input-group">
<textarea class="form-control" asp-for="BannerDescription" aria-label="Banner Description" rows="6"></textarea>
</div>
</div>
</div>
<div class="col-6">
<div class="drop-zone">
<span class="drop-zone__prompt">Drop file here or click to upload</span>
<input type="file" asp-for="BannerImg1" name="myFile" class="drop-zone__input" accept="image/*" data-allowed-file-extensions='["jpg", "png" , "jpeg"]' required>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group c_form_group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"></span>
</div>
<input type="text" class="form-control" asp-for="BannerIndex" placeholder="Banner Index" aria-label="bannerindex" aria-describedby="basic-addon1">
</div>
</div>
</div>
<div class="col-6">
<div class="form-group c_form_group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"></span>
</div>
</div>
</div>
</div>
</div>
<div class="mb-2" align="center">
<button type="submit" class="btn btn-success btn-round">Edit Banner</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
When i try To Edit the data in the browser it Throw exception, in database i have value but when i tried to fetch data its showing error.
thank you in advance to everybody for any help or advice.
According to the error message it is necessary to pass the BannerViewModels instance instead of the Banner entity:
public ActionResult EditBanner(int id)
{
var abd = _db.Banner.Where(a => a.BannerId == id)
.Select(b => new BannerViewModels()
{
BannerId = b.BannerId,
BannerTitle = b.BannerTitle,
BannerDescription = b.BannerDescription,
BannerImg = b.BannerImg,
BannerUrl = b.BannerUrl,
BannerIndex = b.BannerIndex,
BMasterId = b.BMasterId,
IsDeleted = b.IsDeleted
})
.FirstOrDefault();
return View(abd);
}
Passing an entity to the view as a data model is bad idea.
See the following post: Why it's not a good idea to pass entities as Models in MVC?

ASP.NET Core MVC remote validation doesn't hit the controller

I'm using core's remote validation in a form inside a partial view that is loaded in the main view but I keep getting the error in the browser console when trying to execute the validation ([element_name] is the name of the field that's being validated):
Uncaught TypeError: Cannot read property 'call' of undefined. Exception occurred when checking
element [element_name], check the '__dummy__' method.
at a.validator.check (VM23 jquery.validate.min.js:4)
at a.validator.element (VM23 jquery.validate.min.js:4)
at a.validator.onfocusout (VM23 jquery.validate.min.js:4)
at HTMLTextAreaElement.b (VM23 jquery.validate.min.js:4)
at HTMLFormElement.dispatch (jquery.min.js:2)
at HTMLFormElement.v.handle (jquery.min.js:2)
at Object.trigger (jquery.min.js:2)
at Object.simulate (jquery.min.js:2)
at HTMLDocument.i (jquery.min.js:2)
So I have a View that, upon clicking a button, loads a partial view that contains a populated form (to perform update actions) in this <div>:
<div class="modal fade" id="modalPlaceholder" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="false"></div>
with this jQuery code:
function requestUpdate(idSolicitacao) {
$('#modalPlaceholder').empty();
$.ajax({
type: 'POST',
url: 'Solicitacao/GetUpdate/' + idSolicitacao,
success: function (result) {
$('#modalPlaceholder').html(result);
}
});
$('#modalPlaceholder').modal('show');
}
In the partial view form I'm using remote validation to validate the changes the user makes. I've included the jquery-validate scripts here because it was not working when I imported it only in the _Layout.cshtml file. The whole file looks like this:
#model Potinho.ViewModels.SolicitacaoVM.Update
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Fechar">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title"><strong>Solicitação</strong></h4>
</div>
<div class="container-fluid" style="margin:10px">
<div class="row">
<div class="col">
<form asp-controller="Solicitacao" asp-action="Update" method="post">
<div class="form-group">
<input asp-for="#Model.IdSolicitacao" hidden />
<input asp-for="#Model.IdPote" hidden />
<input asp-for="#Model.IdSolicitante" hidden />
<input asp-for="#Model.IdFavorecido" hidden />
<input asp-for="#Model.IdItem" hidden />
<input asp-for="#Model.IdStatus" hidden />
<div class="mb-2">
<label asp-for="#Model.Descricao" class="control-label"></label>
<textarea asp-for="#Model.Descricao" class="form-control"> </textarea>
<span asp-validation-for="#Model.Descricao" class="text-danger"></span>
</div>
<div class="mb-2">
<label asp-for="#Model.Local" class="control-label"></label>
<input asp-for="#Model.Local" class="form-control" />
<span asp-validation-for="#Model.Local" class="text-danger"></span>
</div>
<div class="mb-2">
<label asp-for="#Model.Valor" class="control-label"></label>
<input asp-for="#Model.Valor" class="form-control" />
<span asp-validation-for="#Model.Valor" class="text-danger"></span>
</div>
<div class="mb-2">
<label asp-for="#Model.DataInicio" class="control-label"></label>
<input asp-for="#Model.DataInicio" class="form-control" type="date" />
<span asp-validation-for="#Model.DataInicio" class="text-danger"></span>
</div>
<div class="mb-2">
<label asp-for="#Model.DataFim" class="control-label"></label>
<input asp-for="#Model.DataFim" class="form-control" type="date" />
<span asp-validation-for="#Model.DataFim" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Salvar" class="btn btn-success" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
</div>
The relevant part of the ViewModel like this:
public class Update
{
[BindProperty]
[Remote(action: "IsDescricaoValid", controller: "Solicitacao")]
[Display(Name = "Descrição")]
public string Descricao { get; set; }
[BindProperty]
[Remote(action: "IsLocalValid", controller: "Solicitacao")]
public string Local { get; set; }
[BindProperty]
[Remote(action: "IsValorValid", controller: "Solicitacao")]
public decimal Valor { get; set; }
[BindProperty]
[Remote(action: "IsDataInicioValid", controller: "Solicitacao")]
[Display(Name = "Data de início")]
public DateTime DataInicio { get; set; }
[BindProperty]
[Remote(action: "IsDataFimValid", controller: "Solicitacao")]
[Display(Name = "Data final")]
public DateTime DataFim { get; set; }
[Required]
[BindProperty]
public DateTime DataSolicitacao { get; set; }
[Key]
[Required]
[BindProperty]
public Guid IdSolicitacao { get; set; }
[Required]
[BindProperty]
public Guid IdPote { get; set; }
[Required]
[BindProperty]
public string IdSolicitante { get; set; }
[Required]
[BindProperty]
public string IdFavorecido { get; set; }
[Required]
[BindProperty]
public Guid IdItem { get; set; }
[Required]
[BindProperty]
public Guid IdStatus { get; set; }
}
And the remote validation methods in the controller:
[AcceptVerbs("Get", "Post")]
[AllowAnonymous]
public IActionResult IsDescricaoValid(string descricao)
{
return _validator.IsDescricaoValid(descricao);
}
[AcceptVerbs("Get", "Post")]
[AllowAnonymous]
public IActionResult IsLocalValid(string local)
{
return _validator.IsLocalValid(local);
}
[AcceptVerbs("Get", "Post")]
[AllowAnonymous]
public IActionResult IsValorValid(decimal valor)
{
return _validator.IsValorValid(valor);
}
[AcceptVerbs("Get", "Post")]
[AllowAnonymous]
public IActionResult IsDataInicioValid(DateTime dataInicio)
{
return _validator.IsDataInicioValid(dataInicio);
}
[AcceptVerbs("Get", "Post")]
[AllowAnonymous]
public IActionResult IsDataFimValid(DateTime dataFim)
{
return _validator.IsDataFimValid(dataFim);
}
From what I have tested, the <div> is being loaded with the partial view and the scripts are imported but every field triggers that same error and the breakpoints I have put in the controller aren't even being hit.
Solved it. I was using an outdated (or flawed) jquery-validate import. To solve the problem I replaced the line
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
to
<script src="https://cdn.jsdelivr.net/npm/jquery-validation#1.19.1/dist/jquery.validate.js"></script>
in the _UpdateSolicitacao.cshtml file.

Saving a Html.DropDownList

How can I save into my db this the selected option, Right now It's saving all the data but the ProfileText that is what I need...
I think I need to add an asp-for but I dont know where to be honest, or if it's a different way please tell me.
Here is my view:
#model HCCBPOHR.Data.Candidate
#*#model HCCBPOHR.DomainModel.CandidateModel*#
#*#model HCCBPOHR.Services.CandidateService.PostBoth*#
#{
ViewData["Title"] = "CandidateCreate";
}
<h2>CandidateCreate</h2>
<h4>Candidate</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post" enctype="multipart/form-data" asp-action="CandidateCreate">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Number" class="control-label"></label>
<input asp-for="Number" class="form-control" maxlength="9" />
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group">
#Html.DropDownList("ProfileText", "Select Profile")
</div>
<div class="form-group">
<label asp-for="CV" type="file" class="control-label"></label>
<input asp-for="CV" type="file" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" onclick="this.disabled=true;this.form.submit();" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Here is my Model:
public class Candidate : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string ProfileText { get; set; }
public Byte[] CV { get; set; }
public string CVNAME { get; set; }
public List<Profile> ProfileList { get; set; }
}
This is how I'm sending the List to the View:
public IActionResult CandidateCreate(int postId)
{
using (var applicationcontext = new ApplicationContext())
{
IEnumerable<SelectListItem> items = applicationcontext.Profile.Select(c => new SelectListItem{Value = c.ProfileText,Text = c.ProfileText});
ViewBag.ProfileText = items.ToList();
return View();
}
}
The error that I'm having right now is
NullReferenceException: Object reference not set to an instance of an object.
In your view change the code to:
<div class="form-group">
<select asp-for="ProfileText" asp-items="#Model.ProfileList"></select>
</div>
Then in your model you'll have a property which we can call ProfileText which will then get post to the server when the form is submitted.
Change your model by introducing a new prop as follows:
public SelectList ProfileList { get; set; }
Now in your action your will need to do:
var model = new Candidate();
...
model.ProfileList = new SelectList(YourProfileListFromDbOrSomeWhereElse);
return View(model);
Please note you can also use SelectList(IEnumerable, string dataValueField, string dataTextField)Constructor if you want to set the dataValueField and dataTextField. I do not know how you get your ProfileList and what it contains so hence why I've only made use of the SelectList(IEnumerable items); Constructor.
Further reading here
As I can see, you are not populating the dropdownlist. I think it's better practice to have the values that can be selected(in a SelectList or string list) and a variable that will hold the selected value in your model and use DropDownListFor. the syntax would be:
#Html.DropDownListFor(model => model.ProfileText, new SelectList(Model.ProfileList, "name"), new {(here html attributes)#class = "form-control"})
After doing that you will get the selected value when posting the model

Populating a PartialView's model with content from the Parent page's model in ASP.NET MVC

I am trying to figure out how to populate the Model of my Partial View. For example the PartialView model includes everything the user enters in a notes dialog such as Subject and NoteContent, but it additionally needs the Unique Id of the Model from the parent page which is a Product Id. This way when the note is saved from the dialog window I can save it for that specific product.
In the below example if the user is on the ParentPage viewing a Product with Id 12 and wants to add a note they would click the Notes button to open the dialog window, fill out a subject and content and hit submit. During this submission to the controller EntityModule should = "Product" and EntityKey should = 12 if the ProductId is 12. I am trying to figure out how the NoteViewModel will retrieve these two fields from the parent ViewModel.
ProductViewModel.cshtml
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
NoteViewModel.cshtml
public int Id { get; set; }
public string EntityModule { get; set; }
public int EntityKey { get; set; }
public string Subject { get; set; }
public string Content { get; set; }
ParentPage.cshtml
#model MyApp.Web.ViewModels.Shared.ProductViewModel
#{ await Html.RenderPartialAsync("_NotesPartial"); }
_NotesPartial.cshtml
#model MyApp.Web.ViewModels.Shared.NoteViewModel
<button id="open" class="btn btn-primary">Notes</button>
#(Html.Kendo().Window()
.Name("window")
.Title("About Alvar Aalto")
.Visible(false)
.Draggable()
.Resizable()
.Width(600)
.Actions(actions => actions.Pin().Minimize().Maximize().Close())
.Content(#<text>
<form asp-action="_NotesPartial">
<div class="form-horizontal">
<h4>NoteViewModel</h4>
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Subject" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Subject" class="form-control" />
<span asp-validation-for="Subject" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Content" class="col-md-2 control-label"></label>
<div class="col-md-10">
<textarea asp-for="Content" class="form-control" cols="40" rows="4"></textarea><span asp-validation-for="Content" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
</text>)
)
<script>
$("#open").click(function () {
var win = $("#window").data("kendoWindow");
win.center();
win.open();
});
</script>
One of the ways is to fill and pass the model through the parent page. For example:
public class ProductViewModel {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public NoteViewModel Note { get; set;}
}
Then, use something like this:
#{ await Html.RenderPartialAsync("_NotesPartial", Model.Note); }
There is no RenderAction in MVC6, which was a good solution for such purposes:
Html.RenderAction("_NotesPartial", new { id = ProductViewModel.Id })
but still you can use it in MVC5.

Categories

Resources