When attempting to bind a model to a view, I'm getting a 404 error. The ActionResult is called 'SortDesc' but the view is just a standard 'Index' view. I understand that this isn't loading because the there is now view called 'SortDesc'. However I have two buttons one to sort data ascending and one to sort data decending hence the two seperate functions. I'm just not sure what the best solution would be here, either to continue with the two seperate functions and somehow pass in the correct view to load or to create a new, single HttpPost function on Index that will know which button has been clicked and sort accordingly. Here's the code I have so far:
Models:
public class NumberSetList
{
public int NumberSetListId { get; set; }
public List<NumberList> Numbers { get; set; }
public string SortOrder { get; set; }
}
public class NumberList
{
public int NumberListId { get; set; }
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public int Number4 { get; set; }
public int Number5 { get; set; }
public string SortOrder { get; set; }
}
Razor:
#{
ViewBag.Title = "csi media web test";
}
<div class="jumbotron">
<h1>csi media web test</h1>
<p class="lead">Liane Stevenson</p>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading"><i class="glyphicon glyphicon-arrow-right"></i> Enter Your Four Numbers</div>
<div class="panel-body">
<form class="form-inline">
<div class="col-md-9">
<div class="form-group">
<label class="sr-only" for="number1">1st Number</label>
<input type="number" class="form-control" id="number1" name="Number1" placeholder="#1">
</div>
<div class="form-group">
<label class="sr-only" for="number2">2nd Number</label>
<input type="number" class="form-control" id="number2" name="Number2" placeholder="#2">
</div>
<div class="form-group">
<label class="sr-only" for="number3">3rd Number</label>
<input type="number" class="form-control" id="number3" name="Number3" placeholder="#3">
</div>
<div class="form-group">
<label class="sr-only" for="number4">4th Number</label>
<input type="number" class="form-control" id="number4" name="Number4" placeholder="#4">
</div>
</div>
<div class="col-md-3 text-right">
<a class="btn btn-default" href="#Url.Action("SortDesc", "Home")"><i class="glyphicon glyphicon-arrow-down"></i> Sort Desc</a>
<a class="btn btn-default" href="#Url.Action("SortAsc", "Home")"><i class="glyphicon glyphicon-arrow-down"></i> Sort Asc</a>
</div>
</form>
<p>
#if (Model != null)
{
foreach (int number in Model.Numbers)
{
<span class="label label-info">#number</span>
}
}
</p>
</div>
</div>
</div>
</div>
Home Controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult SortDesc([Bind(Include = "Number1, Number2, Number3, Number4")] NumberSetList model)
{
if (!ModelState.IsValid)
{
return View();
}
else
{
NumberSetList list = new NumberSetList();
List<int> numbers = new List<int>();
numbers.Add(model.Number1);
numbers.Add(model.Number2);
numbers.Add(model.Number3);
numbers.Add(model.Number4);
numbers.OrderByDescending(i => i);
list.SortOrder = "Desc";
return View(list);
}
}
Try this
public class NumberSetList {
public int NumberSetListId { get; set; }
public List<NumberSetItem> Numbers { get; set; }
public string SortOrder { get; set; }
}
public class NumberSetItem {
public int Value { get; set; }
}
, use for loop to create inputs for the numbers
#using (Html.BeginForm()) {
<div class="col-md-9">
<div class="form-group">
#if (Model != null && Model.Numbers != null) {
#for (int i = 0; i < Model.Numbers.Count; i++) {
<div class="form-group">
#Html.LabelFor(m => m.Numbers[i].Value, "Number " + (i+1).ToString())
#Html.TextBoxFor(m => m.Numbers[i].Value, new { #class = "form-control" })
</div>
}
<div class="col-md-3 text-right">
<input id="SortOrder" value="Desc" type="button" class="btn btn-default"><i class="glyphicon glyphicon-arrow-down"></i> Sort Desc</input>
<input id="SortOrder" value="Asc" type="button" class="btn btn-default"><i class="glyphicon glyphicon-arrow-down"></i> Sort Asc</input>
</div>
}
</div>
</div>
}
And in the View
[HttpPost]
public ActionResult Index(NumberSetList model) {
if (!ModelState.IsValid) {
return View();
} else {
var numbers = model.SortOrder == "Desc" ?
model.Numbers.OrderByDescending(n => n.Value) :
model.Numbers.OrderBy(n => n.Value);
model.Numbers = numbers.ToList();
return View(model);
}
}
You can fix the issue using a variety of techniques.
Pass the name of the view into the View method like this:
return View("Index", list);
The View method has many overloads and you can see them all here. It is nice to get yourself familiar with the overloads for scenarios like the one you are facing.
If your view is not in the same folder as the action, you will need to pass the path for the view instead of just the name.
Another way is to have a single action and you can call this one Index as well but this one will take a model as an argument. Your model can have the numbers and an enum for sort: Desc, Asc. In your action method you can analyze the model, and do whatever is needed.
The approach with the model is the one I prefer.
EDIT
I just noticed you have this line of code in your view:
<a class="btn btn-default" href="#Url.Action("SortDesc", "Home")"><i class="glyphicon glyphicon-arrow-down"></i> Sort Desc</a>
That will trigger an HTTP GET and therefore it will not hit your SortDsc action method. Remove POST from your action method or use a post technique (Form) to hit your action method using POST.
EDIT 2
Honestly what you are doing is far simpler than you are making it. First thing is when you are using Url.Actio you are not passing nothing to your action method so how will it know about the numbers? I would normally call my action method using AJAX and get the result and display the result.
Without AJAX, you need to use one form for ascending and one for descending. Or you can use a radio button so user can select the order first.
Here is some code to do it using radio buttons.
Action:
[HttpPost]
public ActionResult SortDesc([Bind( Include = "Number1, Number2, Number3, Number4, SortOrder" )] NumberList model) {
if( !ModelState.IsValid ) {
return View("Index");
}
model.Numbers = new List<int>();
model.Numbers.Add( model.Number1 );
model.Numbers.Add( model.Number2 );
model.Numbers.Add( model.Number3 );
model.Numbers.Add( model.Number4 );
model.Numbers.Add( model.Number5 );
if (model.SortOrder == "Desc" ) {
model.Numbers = model.Numbers.OrderByDescending( i => i ).ToList();
}
else {
model.Numbers = model.Numbers.OrderBy( i => i ).ToList();
}
return View("Index", model);
}
Model:
public class NumberList {
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public int Number4 { get; set; }
public int Number5 { get; set; }
public int NumberListId { get; set; }
public List<int> Numbers { get; set; }
public string SortOrder { get; set; }
}
View:
#using( Html.BeginForm( "SortDesc", "Home", FormMethod.Post ) ) {
<div class="row">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading"><i class="glyphicon glyphicon-arrow-right"></i> Enter Your Four Numbers</div>
<div class="panel-body">
<form class="form-inline">
<div class="col-md-9">
<div class="form-group">
<label class="sr-only" for="number1">1st Number</label>
<input type="number" class="form-control" id="number1" name="Number1" placeholder="#1">
</div>
<div class="form-group">
<label class="sr-only" for="number2">2nd Number</label>
<input type="number" class="form-control" id="number2" name="Number2" placeholder="#2">
</div>
<div class="form-group">
<label class="sr-only" for="number3">3rd Number</label>
<input type="number" class="form-control" id="number3" name="Number3" placeholder="#3">
</div>
<div class="form-group">
<label class="sr-only" for="number4">4th Number</label>
<input type="number" class="form-control" id="number4" name="Number4" placeholder="#4">
</div>
<div class="form-group">
<input type="radio" class="form-control" id="number4" name="SortOrder" value="Desc">
</div>
<div class="form-group">
<input type="radio" class="form-control" id="number4" name="SortOrder" value="Asc">
</div>
</div>
<div class="col-md-3 text-right">
<button class="btn btn-default"><i class="glyphicon glyphicon-arrow-down"></i> Sort </button>
#*<a class="btn btn-default"><i class="glyphicon glyphicon-arrow-down"></i> Sort Asc</a>*#
</div>
</form>
<p>
#if( Model != null ) {
foreach( int number in Model.Numbers ) {
<span class="label label-info">#number</span>
}
}
</p>
</div>
</div>
</div>
</div>
}
Related
Having started studying asp net core 6, I ran into the problem of invalidity of my model.
What is the hidden problem?
Model of Product
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TestWebApplication.Models
{
public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Range(0.1, int.MaxValue)]
public double Price { get; set; }
public string Image { get; set; }
[Display(Name = "Category Type")]
public int CategoryId { get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
}
}
Model of ProductVM
using Microsoft.AspNetCore.Mvc.Rendering;
namespace TestWebApplication.Models.ViewModels
{
public class ProductVM
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> CategorySelectList { get; set; }
}
}
View
#model TestWebApplication.Models.ViewModels.ProductVM
#{
var title = "Create Product";
}
<form method="post" enctype="multipart/form-data">
#if(Model.Product.Id != 0)
{
title = "Edit Product";
<input asp-for="Product.Id" hidden />
}
<div class="border p-3">
<div class="form-group row">
<h2 class="text-info pl-3">#title</h2>
</div>
<br />
<div class="row">
<div class="col-8">
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Name"></label>
</div>
<div class="col-8">
<input asp-for="Product.Name" class="form-control"/>
<span asp-validation-for="Product.Name" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Price"></label>
</div>
<div class="col-8">
<input asp-for="Product.Price" class="form-control"/>
<span asp-validation-for="Product.Price" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Description"></label>
</div>
<div class="col-8">
<textarea asp-for="Product.Description" class="form-control summernote"> </textarea>
<span asp-validation-for="Product.Description" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
Image
</div>
<div class="col-8">
<input type = "file" name="files" id="uploadBox" multiple class="form-control"/>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.CategoryId"></label>
</div>
<div class="col-8">
<select asp-for="Product.CategoryId" asp-items="#Model.CategorySelectList" class="form-control">
<option disabled selected>---Select Category---</option>
</select>
<span asp-validation-for="Product.CategoryId" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-8 offset-4 row">
<div class="col">
#if (Model.Product.Id != 0)
{
//update
<input type="submit" class="btn btn-info w-100" value="Update"/>
}
else
{
//create
<input type="submit" onclick="return validateInput()" class="btn btn-primary w-100" value="Create"/>
}
</div>
<div class="col">
<a asp-action="Index" class="btn btn-success w-100">
Back
</a>
</div>
</div>
</div>
</div>
<div class="col-4">
#* Keep this empty *#
</div>
</div>
</div>
</form>
#section Scripts
{
#{
<partial name= "_ValidationScriptsPartial.cshtml"/>
}
<script>
$(document).ready(function() {
$('.summernote').summernote(
{
height:250
});
});
function validateInput()
{
if (document.getElementById("uploadBox").value == "")
{
Swal.fire
(
'Error!',
'Please, upload an image',
'error'
)
return false;
}
return true;
}
</script>
}
Controller
using TestWebApplication.Data;
using TestWebApplication.Models;
using TestWebApplication.Models.ViewModels;
namespace TestWebApplication.Controllers
{
public class ProductController : Controller
{
private readonly ApplicationDbContext _db;
private readonly IWebHostEnvironment _webHostEnvironment;
public ProductController(ApplicationDbContext db, IWebHostEnvironment webHostEnvironment)
{
_db = db;
_webHostEnvironment = webHostEnvironment;
}
public IActionResult Index()
{
IEnumerable<Product> objList = _db.Product;
foreach (var item in objList)
{
item.Category = _db.Category.FirstOrDefault(category => category.Id == item.Id);
}
return View(objList);
}
// get - upsert
public IActionResult Upsert(int? id)
{
//IEnumerable<SelectListItem> CategoryDropDown = _db.Category.Select(i => new SelectListItem
//{
// Text = i.Name,
// Value = i.Id.ToString()
//});
//ViewBag.CategoryDropDown = CategoryDropDown;
//Product product = new Product();
ProductVM productVM = new ProductVM()
{
Product = new Product(),
CategorySelectList = _db.Category.Select(i => new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString()
})
};
if(id == null)
{
// for create
return View(productVM);
}
else
{
productVM.Product = _db.Product.Find(id);
if (productVM.Product == null)
NotFound();
return View(productVM);
}
}
// post - upsert
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Upsert(ProductVM obj)
{
if (ModelState.IsValid)
{
var files = HttpContext.Request.Form.Files;
string webRootPath = _webHostEnvironment.WebRootPath;
if(obj.Product.Id == 0)
{
//Creating
string upload = webRootPath + WC.ImagePath;
string fileName = Guid.NewGuid().ToString();
string extention = Path.GetExtension(files[0].FileName);
using(var fileStream = new FileStream(Path.Combine(upload, fileName + extention), FileMode.Create))
{
files[0].CopyTo(fileStream);
}
obj.Product.Image = fileName + extention;
_db.Product.Add(obj.Product);
}
else
{
//Updating
}
return RedirectToAction("Index");
}
return View();
}
}
List of fields that participated in validation
From one answer, I realized that the Category field will remain null, and ef core will substitute the values. But I don't quite understand why so many fields are validated without being marked required in the model.
By receives a POST action, ModelState takes all of the name-value pairs and adds them as individual instances of ModelStateEntry to an instance of ModelStateDictionary.
ModelState checks all properties regardless of whether they have a Data annotations or not, such as you get a number, text, file etc. But for those data annotations you put on the properties the ModelState makes a special check based on it.
Just the properties of the model you passed to controller would be validated,you could try custom model validation follow this document:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0#custom-attributes
And you could try with ModelState.AddModelError(string.Empty, "errormessage");add model level error in backend.
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?
I'm trying to get a value from each dropdownlist in the view model. I'm doing with a foreach but that doesn't seem to work. Can anyone help me here please?
I'm doing in the controller the foreach. I'm using Entity Framework and repository methods to this. Any help is helpful.
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(int? id, AddSaidasServicoViewModel model, string command)
{
if (ModelState.IsValid)
{
//TODO: adicionar Userhelper para ver quem cria a saida. Criar campo na tabela BD. Ver apontamento Notepad
if (command.Equals("submit1"))
{
await _saidaservicoRepository.AddSaidaServicoAsync(model);
return RedirectToAction(nameof(Create));
}
else
{
foreach(var elementoId in model.Elementos )
{
await _elementoRepository.UpdateElementoSaidaServicosAsync(model);
}
}
return RedirectToAction(nameof(Index));
}
return View(model);
}
View markup:
#model FireHouseGest.web.Models.AddSaidasServicoViewModel
#{
ViewData["Title"] = "Create";
}
<h2>Criar</h2>
<h4>Saída Serviço</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="container">
<div class="row">
<div class="text-center">
<div class="col-md-3 text-center">
<div class="form-group">
<label asp-for="ServicoId" class="control-label"></label>
<select asp-for="ServicoId" asp-items="Model.Servicos" class="form-control"></select>
<span asp-validation-for="ServicoId" class="text-danger"></span>
</div>
</div>
<div class="col-md-3 text-center">
<div class="form-group">
<label asp-for="ViaturaId" class="control-label"></label>
<select asp-for="ViaturaId" asp-items="Model.Viaturas" class="form-control"></select>
<span asp-validation-for="ViaturaId" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="form-group">
<input type="submit" name="command" value="submit1" class="btn btn-default" />
</div>
</div>
<div class="container">
<div class="row">
<div class="col-lg-2 text-center">
<div class="form-group">
<label asp-for="ElementoId" class="control-label"></label>
<select asp-for="ElementoId" asp-items="Model.Elementos" class="form-control"></select>
<span asp-validation-for="ElementoId" class="text-danger"></span>
</div>
</div>
<div class="col-lg-2 text-center">
<div class="form-group">
<label asp-for="ElementoId" class="control-label"></label>
<select asp-for="ElementoId" asp-items="Model.Elementos" class="form-control"></select>
<span asp-validation-for="ElementoId" class="text-danger"></span>
</div>
</div>
<div class="col-lg-2 text-center">
<div class="form-group">
<label asp-for="ElementoId" class="control-label"></label>
<select asp-for="ElementoId" asp-items="Model.Elementos" class="form-control"></select>
<span asp-validation-for="ElementoId" class="text-danger"></span>
</div>
</div>
<div class="col-lg-2 text-center">
<div class="form-group">
<label asp-for="ElementoId" class="control-label"></label>
<select asp-for="ElementoId" asp-items="Model.Elementos" class="form-control"></select>
<span asp-validation-for="ElementoId" class="text-danger"></span>
</div>
</div>
<div class="col-lg-2 text-center">
<div class="form-group">
<label asp-for="ElementoId" class="control-label"></label>
<select asp-for="ElementoId" asp-items="Model.Elementos" class="form-control"></select>
<span asp-validation-for="ElementoId" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<input type="submit" name="command" value="submit2" class="btn btn-default" />
</div>
</div>
<div class="form-group">
<label asp-for="Adecorrer" class="control-label"></label>
<input asp-for="Adecorrer" class="form-control" />
<span asp-validation-for="Adecorrer" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Inicio" class="control-label"></label>
<input asp-for="Inicio" class="form-control" />
<span asp-validation-for="Inicio" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Fim" class="control-label"></label>
<input asp-for="Fim" class="form-control" />
<span asp-validation-for="Fim" class="text-danger"></span>
</div>
</form>
</div>
</div>
ViewModel
namespace FireHouseGest.web.Models
{
public class AddSaidasServicoViewModel
{
public int SaidaServicoId { get; set; }
[Display(Name = "Elementos")]
public int ElementoId { get; set; }
public IEnumerable<SelectListItem> Elementos { get; set; }
[Display(Name = "Viatura")]
public int ViaturaId { get; set; }
public IEnumerable<SelectListItem> Viaturas { get; set; }
[Display(Name = "Serviço")]
public int ServicoId { get; set; }
public IEnumerable<SelectListItem> Servicos { get; set; }
[Display(Name = "Estado")]
public int Adecorrer { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy hh:mm }", ApplyFormatInEditMode = false)]
public DateTime Inicio { get { return DateTime.Now; } }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy hh:mm }", ApplyFormatInEditMode = false)]
public DateTime? Fim { get; set; }
public User user { get; set; }
}
}
This is what I have. Open to suggestions.
Before coding, you need know the following things:
1.Model Binding find and bind the property by matching the name of the input.
2.If you have multiple inputs with the same name, you need set a List<T> type parameter to receive all the values.
3.Dropdownlist gets the selected option value(not text),be sure the type of the value is the same with the type of matched property. (e.g. ElementoId is int type,so the value of each SelectListItem in Elementos should be int or it could be converted to int):
var model = new AddSaidasServicoViewModel()
{
Elementos = new List<SelectListItem>() {
new SelectListItem() { Value = "1", Text = "Elementos1" },
new SelectListItem() { Value = "2", Text = "Elementos2" },
new SelectListItem() { Value = "3", Text = "Elementos3" }
},
//....
};
4.Dropdownlist selected value will match the property by searching the same name of the asp-for="xxx". (e.g. asp-for="ElementoId" will generate html:id="ElementoId" name="ElementoId",so this input value will match the property ElementoId in your model)
5.Although you have two submit button, but the submit button will always submit all the input values which they are in the same form.
Change your code like below and you will receive all the selected ElementoId values in List<string> ElementoId:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(int? id, AddSaidasServicoViewModel model,
List<string> ElementoId, string command)
{
if (ModelState.IsValid)
{
//TODO: adicionar Userhelper para ver quem cria a saida. Criar campo na tabela BD. Ver apontamento Notepad
if (command.Equals("submit1"))
{
//...
}
else
{
foreach (var elementoId in ElementoId)
{
//....
}
}
return RedirectToAction(nameof(Index));
}
return View(model);
}
Thanks Rena for answer. this was what i did.
public async Task UpdateElementosSaidasAsync(AddSaidasServicoViewModel model, List<string> Elementos)
{
int SaidaServicos = _context.SaidaServicos.Max(item => item.Id);
var Saida = await _context.SaidaServicos.FindAsync(SaidaServicos);
if (Elementos == null)
{
return;
}
else
{
foreach (var elementoId in Elementos)
{
var updateElementoSaida = _context.Elementos.Where(e => e.Id == Convert.ToInt32(elementoId)).FirstOrDefault();
updateElementoSaida.saidaServico = Saida;
await _context.SaveChangesAsync();
}
}
return;
}
Is my ViewModel flow right? The 'SubObjectViewModel' class usage is a bad or a good practice? Or should I try other option, like creating a ViewModel only to this class?
And also, how should I return the ViewModel 'ObjectViewModel' to the controller with all its values, change them and refresh values from page view?
My ViewModel
Public class ObjectViewModel{
public string name { get; set; }
public int value { get; set; }
public bool IsCameraPermited { get; set; }
public List<SubObjectViewModel> choosenSubObjects{ get; set; } // need to get it back on controller;
public class SubObjectViewModel
{
public int IdObject { get; set; }
public string Name { get; set; }
public string Config { get; set; }
}
public List<Object> listSub{ get; set; } //list that will be filled on 'Create' Controller
}
My Controller
public IActionResult Create(int id)
{
List<Object> listSubObject = new List<Object>();
listSubObject = _getAllSubObjectsDAO.ListByID(id);
List<Object> choosenObjects= new List<Object>();
choosenObjects = _getChoosenObjectsDAO.ListByID(id);
List<SubObjectViewModel> listSubObject = new List<SubObjectViewModel>();
foreach (Object item in choosenObjects )
{
string config = _configurationDAO.GetConfigurationById(item.configId);
ObjectViewModel .SubObjectViewModel SubObject = new ObjectViewModel .SubObjectViewModel { IdObject = item.Id, Name = item.Name , Config = config };
listSubObject.Add(setorVM);
}
ObjectViewModel objVM = ObjectViewModel{
name ="test",
value = 2,
IsCameraPermited =true,
listSub = listSubObject,
choosenSubObjects = listSubObject
};
return View(objVM);
}
My View
#model Project.Models.ViewModels.ObjectViewModel
... more code
<form asp-action="Create">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#foreach (var item in listSub)
{
<div class="txt">
#item
</div>
}
</div>
</div>
</div>
</form>
Thanks in advance.
how should I return the ViewModel 'ObjectViewModel' to the controller with all its values
To achieve above requirement, you can refer to the following code snippet.
#model ObjectViewModel
#{
ViewData["Title"] = "Create";
var choosenSubObjects = Model.choosenSubObjects;
}
<form method="post">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#for (var i = 0; i < Model.choosenSubObjects.Count; i++)
{
<div class="txt">
<div class="form-group">
<label asp-for="#choosenSubObjects[i].IdObject" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].IdObject" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Name" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Name" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Config" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Config" type="text" />
</div>
</div>
}
</div>
</div>
</div>
</form>
Controller Actions
[HttpGet]
public IActionResult Create(int id)
{
//code logic here
//...
return View(objVM);
}
[HttpPost]
public IActionResult Create(ObjectViewModel objectViewModel)
{
//code logic here
//...
Test Result
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