ASP .NET ViewModel error - include

I have a problem . When he calls AssortmentController thus "/ assortment/kindslist?kindname=horror"
I get the error:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[Isf.Models.Movie]', but this
dictionary requires a model item of type
'Isf.ViewModels.HomeViewModel'.
This is my AssortmentController:
public class AssortmentController : Controller
{
AssortmentRelationships db = new AssortmentRelationships();
// GET: Assortment
public ActionResult Index()
{
return View();
}
public ActionResult ExplicationMovie(string moviename)
{
return View();
}
public ActionResult KindsList(string kindname)
{
var kinds = db.Kinds.Include("Movies").Where(k => k.KindName.ToUpper() == kindname.ToUpper()).Single();
var movies = kinds.Movies.ToList();
return View(movies);
}
[ChildActionOnly]
public ActionResult KindsMenu()
{
var kinds = db.Kinds.ToList();
return PartialView("_KindsMenu", kinds);
}
}
KindsList view:
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-2" id="right-list">
#Html.Action("KindsMenu")
</div>
<div class="col-xs-12 col-md-10">
test
</div>
</div>
</div>
_KindsMenu view:
#model IEnumerable<Isf.Models.Kind>
<ul>
#foreach (var kind in Model)
{
<li>
<a href="#">
<button class="btn btn-default" type="button">aaa</button>
</a>
</li>
}
</ul>
_MoviesList view:
#model Isf.Models.Movie
<div class="remove-all-margin-padding" id="category_view">
<img class="hover_detail" src="~/img/covers/1.jpg" />
</div>
PartialView is ok. All is ok if I comment KindsList action...
I dont use viewmodel because I use html.action in KindsList view...
I've done so many times and it was always ok

Related

ASP.NET MVC - data validation for modal window doesn't work

In my MVC application I have simple model for project:
public class Project
{
public int Id { get; set; }
[Required]
[MinLength(5, ErrorMessage = "Project name must have at leat 5 characters")]
public string ProjectName { get; set; }
[Required]
[MinLength(10, ErrorMessage = "Project description name must have at leat 10 characters")]
public string ProjectDescription { get; set; }
public ICollection<BugTrackerUser> Users { get; set; }
public Project()
{
Users = new List<BugTrackerUser>();
}
}
On the index page I have button that triggers bootstrap modal window. As a button, modal itself located in Index.chtml, but form that belongs to this model located in partial view _addProjectPartialView:
<form method="post" id="projectForm">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectName">Project Name</label>
<input asp-for="ProjectName" class="form-control" />
<span asp-validation-for="ProjectName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectDescription">Project Description</label>
<input asp-for="ProjectDescription" class="form-control" />
<span asp-validation-for="ProjectDescription" class="text-danger"></span>
</div>
In HomeController I have this post method for adding new project:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddProject(Project project)
{
if (ModelState.IsValid)
{
_repository.AddProject(project);
return RedirectToAction("Index", "Home");
}
return PartialView("_addProjectPartialView", project);
}
Logic of adding new project works fine, and I have all references that i need, jquery, jquery-validation and jquery-validation-unobtrusive, but I have problems with validation. Validation errors doesn't displays in my modal window. What can I try to solve this problem? I already read a bunch of tutorials, related questions etc, but it seems to me that there is no problems with code. I only have doubts with my return statement in AddProject method, should I return something else?
I solved this problem by removing partial view. Form that I Have in partial view and modal window I placed in Index.cshtml, also instead of asp.net tag helpers I use Html.BeginForm, and it works fine. Here is my modal:
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModallLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
#using (Html.BeginForm("AddProject", "Home", FormMethod.Post))
{
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Project Information</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(p => p.Id)
<div class="form-group">
#Html.LabelFor(p => p.ProjectName, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectName, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(p => p.ProjectDescription, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectDescription, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectDescription, "", new { #class = "text-danger" })
</div>
</div>
#*<partial name="_addProjectPartialView" />*#
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="btnSave">Save</button>
</div>
</div>
}
</div>
</div>
I also using ajax post method to add projects and render list of projects async on the client side:
<script type="text/javascript">$(document).ready(function () {
$('#btnSave').click(function () {
var projectData = $('#projectForm').serialize();
$.ajax({
type: "POST",
url: "/Home/AddProject",
data: projectData,
success: function () {
window.location.href = "/Home/Index";
}
})
})
})
$('#closeButton').click(function () {
$('#addProjectModal').modal('hide');
})</script>
But I still want to understand how to solve this problem, because using partial views have some benefits
I think the problem here is , when you triggers the modal that modal doesn't have the references to (validator unobtrusive).
So try manually register this where you triggers the modal like this
$.validator.unobtrusive.parse("#idForModal");

How to set input type="file" from model in razor

I have a multi step form that has multiple input with type of file.
in some step I call submit button and again I get back to same page without uploading any files because I dont want to upload these files in this step. for example user enters all information and select files that he wants and after that I go to controller and get back to page to show him confirmation page.
how can I fill input files with model properties? and again he submit the page that file property shouldnt be empty.
<input asp-for="CertImageFile" type="file" accept=".jpg, .jpeg, .png">
Model
public IFormFile CertImageFile { get; set; }
You can try to use ajax and partial view:
MyModelClass:
public class MyModelClass
{
public IFormFile CertImageFile { get; set; }
}
View:
#model MyModelClass
<div class="row">
<div class="col-md-6">
<div>
<input asp-for="CertImageFile" type="file" accept=".jpg, .jpeg, .png">
</div>
<button onclick="Confirm()">confirm</button>
</div>
<div id="modal"></div>
</div>
#section scripts{
<script type="text/javascript">
function Confirm() {
$.ajax({
url: "TestPartial",
type: "POST",
success: function (data) {
//put partial view to div
$("#modal").html(data);
$("#cartModal").modal('show');
}
});
}
function Submit() {
var pdata = new FormData();
var files = $("#CertImageFile").get(0).files;
pdata.append('CertImageFile', files[0]);
$.ajax({
url: "Submit",
type: "POST",
data: pdata,
processData: false,
contentType: false,
success: function (data) {
//you can do something or redirect to other page after post the data
}
});
}
</script>
}
Controller:
[HttpPost]
public IActionResult Save(MyModelClass myModelClass)
{
return Ok();
}
_Partial:
<div class="modal fade cart-modal" id="cartModal" tabindex="-1" role="dialog" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
Tip:
</div>
<div class="modal-body">
Do you want to submit the file?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="Submit()">submit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Controller:
[HttpPost]
//pass IFormFile to action
public IActionResult Submit(MyModelClass myModelClass)
{
return Ok();
}
//view call action to pass partial view
public IActionResult TestPartial() {
return PartialView("_Partial");
}
result:

Custom validation message not shown using IValidatableObject

I have a simple form with 2 buttons (Cancel and Submit) and a TextArea. The user types a list of email addresses and presses submit. I am trying to figure out why my custom message is not being shown when I submit my form. I know the validation logic works as it triggers my [Required] rule and I can see the error message for that:
However, when I type in data such as "test#" and then submit, the logic in the Validate gets triggered but I can't see my error message "Please make sure that all of the emails are valid". What am I doing wrong?
That is my Model:
public class ShareModel : IValidatableObject
{
[HiddenInput] public string Title { get; set; }
[Required]
public string Emails { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// custom validation logic here
yield return new ValidationResult($"Please make sure that all of the emails are valid", new[] { "Emails" });
}
}
That is my view:
<div class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new {#id = "partialform"}))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title"/>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Share</button>
<button id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
According to this post IValidatableObject is not considered on client-side. In order to display custom error message from custom validation you need to implement custom ValidationAttribute that also implements IClientModelValidator interface as described here.
For future reference, as Alexander explained above I had to use both ValidationAttribute, IClientModelValidator like that:
ShareModel:
public class ShareModel
{
[HiddenInput] public string Title { get; set; }
[Required]
[IsEmailAttribute(ErrorMessage = "Check all of the emails you have typed")]
public string Emails { get; set; }
}
public class IsEmailAttribute : ValidationAttribute, IClientModelValidator
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return new ValidationResult("Check emails!");
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-isEmail", errorMessage);
}
private bool MergeAttribute(
IDictionary<string, string> attributes,
string key,
string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
_ShareView.cshtml:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.ShareModel
<!-- Modal -->
<div class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new {#id = "partialform"}))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title"/>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Share</button>
<button id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
index.cshtnl:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkBookModel
#{
}
#section BodyFill
{
<div id="shareFormContainer">
<!--PLACEHOLDER FOR SHARE DIALOG -->
#{
#Html.Partial("_ShareView", new ShareModel())
}
</div>
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
<tr>
<td>#Html.ActionLink(workbook.Name, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td>
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
function showSharingView(title) {
var url = "#Url.Action("ShowShareDialog", "Home")" + "?workbookTitle=" + encodeURI(title);
$('#shareFormContainer').load(url,
function() {
$('#shareFormModal').modal("show");
// // We need to manually register the form for validation as the dialog is
// // not included in the page when it initially loads
$.validator.unobtrusive.parse("#partialform");
// email validation
$.validator.addMethod("isEmail",
function (value, element, parameters) {
// TODO CLIENT SIDE VALIDATETION LOGIC HERE
return false;
});
$.validator.unobtrusive.adapters.add("isEmail",
[],
function(options) {
options.rules.isEmail = {};
options.messages["isEmail"] = options.message;
});
});
}
</script>
}

ApplicationDbContext.Update() doesn't update but saves it as a new record

I am playing in the new APS.NET 5 RC1 environment but the default edit action creates a new entity of the object. I am searched the whole day finding where it goes wrong but I can't find out why it goes wrong.
Controller:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using SSTv5.Models;
using SSTv5.Models.Organisations;
using SSTv5.Models.Views;
using System.Collections.Generic;
using SSTv5.Models.Global;
namespace SSTv5.Controllers
{
public class OrganisationTypesController : Controller
{
private ApplicationDbContext _context;
private List<Breadcrumb> _bcList = new List<Breadcrumb>();
public OrganisationTypesController(ApplicationDbContext context)
{
_context = context;
}
#region Edit
// GET: OrganisationTypes/Edit/5
public async Task<IActionResult> Edit(int? id)
{
_bcList.Add(new Breadcrumb("OrganisationTypes", true, "Index", "Index"));
_bcList.Add(new Breadcrumb("Edit", false));
ViewBag.BcList = _bcList;
if (id == null)
{
return HttpNotFound();
}
OrganisationType organisationType = await _context.OrganisationType.SingleAsync(m => m.OrganisationTypeId == id);
if (organisationType == null)
{
return HttpNotFound();
}
return View(organisationType);
}
// POST: OrganisationTypes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(OrganisationType organisationType)
{
if (ModelState.IsValid)
{
_context.Update(organisationType);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(organisationType);
}
#endregion
}
}
Form:
#model SSTv5.Models.Organisations.OrganisationType
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>OrganisationType</h4>
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrganisationTypeName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OrganisationTypeName" class="form-control" />
<span asp-validation-for="OrganisationTypeName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="ClassIcon" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ClassIcon" id="classIcon" />
<span asp-validation-for="ClassIcon" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}
For anyone else who has this problem the answer is quite simple.
The form doesn't send the ID of the object with it. The only thing you need to do is put the id in a hidden field in the form. Then it will work fine.

Ajax GET request calling POST instead (ASP.NET MVC5)

I've created an ajax GET call to perform a search feature. But every time the search button is clicked, it is calling the POST instead (thereby returning null error for the model). I am not sure what I have done wrong. Care to give a hand please?
My controller:
//
// GET: /DataEntry/ChargeBack
public ActionResult ChargeBack(string dwName, string searchTerm = null)
{
var model = createModel();
if (Request.IsAjaxRequest())
{
return PartialView("_Suppliers", model);
}
return View(model);
}
//
// POST: /DataEntry/ChargeBack
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ChargeBack(ChargeBackViewModel model)
{
if (ModelState.IsValid)
{
model.someAction();
}
return View(model);
}
My Main View:
#model CorporateM10.Models.ChargeBackViewModel
#using (Ajax.BeginForm(
new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "SuppliersList"
}))
{
#Html.AntiForgeryToken()
<input type="search" name="searchTerm" class="form-control col-md-2" />
<input type="submit" value="Search by Name" class="btn btn-info" />
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#Html.Partial("_Suppliers", Model)
<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>
}
My Partial View:
#model CorporateM10.Models.ChargeBackViewModel
<div class="form-group" id="SuppliersList">
<div class="col-md-10">
#Html.EditorFor(model => model.Suppliers)
#Html.ValidationMessageFor(model => model.Suppliers)
</div>
</div>
I've found it. It is being caused by not including jquery.unobtrusive-ajax library. Once included, the Ajax action works as intended.

Resources