What is my mistake? AllFindedBrands property is not passing to SearchBrandResult Action of Controller
Controller:
public async Task<IActionResult> Search(string Articul, int idClient)
{
List<BrandList> findedBrands = new List<BrandList>();
#ViewBag.list = woDupes;
SearchViewModel model = new SearchViewModel();
model.Articul = Articul;
model.idClient = idClient;
model.AllFindedBrands = new List<BrandList>(findedBrands);
return View(model);
}
[HttpPost]
public async Task<IActionResult> SearchBrandResult(SearchViewModel model)
{
return View();
}
View:
<form asp-controller="Search" asp-action="SearchBrandResult" asp-route-
Articul="#Model.Articul"
asp-route-AllFindedBrands="#Model.AllFindedBrands" asp-route-
idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" asp-items="#(new SelectList(#ViewBag.list,
nameof(FindedBrand.Name),
nameof(FindedBrand.Name)))"
multiple="true" class="form-control brand-chosen">
</select>
<input type="submit" />
All other properties of ViewModel is successfully passed to th Action
AllFindedBrands is type of complex model and asp-route-* cannot dynamic bind the value. You can F12 in the browser to check the generated url in the form.
Two ways you can follow:
1.By using asp-all-route-data and foreach the AllFindedBrands to bind the value which passes the value by route data.
Assume your model like below:
public class SearchViewModel
{
public string Articul { get; set; }
public string idClient { get; set; }
public List<BrandList> AllFindedBrands { get; set; }
public List<string> SelectedBrand { get; set; }
}
public class BrandList
{
public string Name { get; set; }
}
View(For easy testing, I just hard-coded the dropdownlist):
#model SearchViewModel
#{
var data = new Dictionary<string, string>();
for(int i=0;i<Model.AllFindedBrands.Count();i++)
{
data.Add("AllFindedBrands[" + i + "].Name", Model.AllFindedBrands[i].Name);
}
}
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-all-route-data="#data" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>
2.By listing the property and make them hidden inputs which passes the value by form data:
#model SearchViewModel
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
#for (int i = 0; i < Model.AllFindedBrands.Count(); i++)
{
<input asp-for="#Model.AllFindedBrands[i].Name" hidden />
}
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>
Related
I have two Entities that have many To many relationship, Book and Tags asp Created automatically a table for the two entities, Using the code first approach
I am trying to add a collection of tags in the book creation, but the tag items are null also there is (select asp-for="Tags" ) but it shows me null in [httppost]create.
I tried to add it in through context as it catches the values of tags I add, but there is an error
cannot convert ......dbset<> to Models.tags
Code:
public class Book
{
public int BookID { get; set; }
[Required]
public string Name { get; set; } = null!;
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; }
}
public class Tags
{
public int TagsID { get; set; }
public string TagName { get; set; } = null!;
//Navigation property
public virtual ICollection<Book>? Book { get; set; }
}
//DB Context
public class BLabContext: DbContext
{
public DbSet<Book> Book { get; set; }
public DbSet<Tags> Tags { get; set; }
}
// Book Controller
public class BooksController : Controller
{
private readonly BLabContext _context;
public BooksController(BLabContext context)
{
_context = context;
}
// Tags objects
// public ICollection<Tags> Tags { get; set; }
// GET: Books
public async Task<IActionResult> Index()
{
return View(await _context.Book.ToListAsync());
}
// GET: Books/Create
(on get )
public IActionResult Create()
{
///πππππππ
var tags = _context.Tags.ToList();
ViewData["tags"] = tags;
//ViewBag.tags = tags;
return View();
}
// POST: Books/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("BookID,Name,Description,PublishedOn,Publisher,Price,ImageURL,1)πTagsπ")] Book book)
{
if (ModelState.IsValid)
{
2)π
var tags = _context.Tags;
_context.Add(book);
await _context.SaveChangesAsync();
2)π(cannot convert ......dbset<> to Models.tags
_context.Book.FirstOrDefault(b => b.BookID == book.BookID).Tags.Add(tags);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
The create view:
#using Book_Library.Models;
#model Book_Library.Models.Book
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<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="Tags" class="control-label"></label>
#*name="Tags"*#
<select asp-for="Tags" multiple>
#foreach (var tag in #ViewData["tags"] as IList<Tags>)
{
<option value="#tag.TagName">#tag.TagName </option>
}
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I expect to add a collection of tags to the book on creation, and also view those tags on the details view
You can create a ViewModel to achieve it, Please refer to this demo:
ViewModel
public class CreateBookViewModel
{
[Required]
public string BookName { get; set; }
public List<int> TagId { get; set; }
}
Controller
public IActionResult Create()
{
var tags = _context.Tags.ToList();
List<SelectListItem> dropdown = new List<SelectListItem>();
foreach (var item in tags)
{
var listItem = new SelectListItem();
listItem.Text = item.TagName;
listItem.Value = item.TagsID.ToString();
dropdown.Add(listItem);
}
ViewBag.tags = dropdown;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateBookViewModel book)
{
if (ModelState.IsValid)
{
Book b = new Book();
b.Name = book.BookName;
if (book.TagId.Count>0)
{
foreach (var item in book.TagId)
{
var tag = _context.Tags.Where(x => x.TagsID == item).FirstOrDefault();
b.Tags.Add(tag);
}
}
_context.Book.Add(b);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
View
#model CreateBookViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="BookName" class="control-label"></label>
<input asp-for="BookName" class="form-control" />
<span asp-validation-for="BookName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.TagId" class="control-label"></label>
<select asp-for="#Model.TagId" asp-items="#ViewBag.tags" multiple></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Now, You can add collection of Tags to Book successfully.
Note: You need to instantiate Tags in your Book class, Otherwise the above code will report a nullreferenceexception.
public class Book
{
//.........
//Navigation property
public virtual ICollection<Tags>? Tags { get; set; } = new List<Tags>();
}
I am very new to this and I am trying to show multiple entries to show and have a single button for each entry to edit them. I thought it would be really easy but For some reason I am not getting the clicked entity. Is there way of getting modified entity without running javascript?
Any help will be appreciated. I couldn't find any example code that does this. There are many examples that returns all the entries but not the single element.
Here is my entity class Resource:
public class Resource
{
[Required]
public string title { get; set; }
[Required]
public int value { get; set; }
[Key]
[Required]
public int id { get; set; }
}
On the controller side I have:
[HttpGet]
public IActionResult Index()
{
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return View(resource);
}
Here is the View and EditorTemplates
#model learningMVC.Models.Resource[]
#{
ViewData["Title"] = "index";
}
<h1>index</h1>
<fieldset>
#Html.EditorFor(x => x);
</fieldset>
#addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
#model learningMVC.Models.Resource
<div>
First Name:<br />
<form asp-action="Index" asp-controller="Home">
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="Save" class="btn btn-primary" id="btnSubmit_#Model.id" />
</form>
</div>
In your case, you should use particalview instead of editfor, you can see my demo below.
_ResourcesPartical.cshtml:
#model Resource
<div>
<form method="post" asp-action="Index">
First Name:<br />
<input asp-for="id" class="form-controller" />
<input asp-for="value" class="form-controller" />
<input asp-for="title" class="form-controller" />
<input type="submit" value="save" />
</form>
</div>
Your Index view:
#model List<Resource>
#foreach(var m in Model)
{
#await Html.PartialAsync("_ResourcesPartical", m)
}
Controller:
[HttpGet]
public IActionResult Index()
{
//here you get your data.below is my fake data.
var resources= new List<Resource>
{
new Resource
{
id = 1,
title = "aa",
value = 3
},
new Resource
{
id = 2,
title = "bb",
value = 4
}
};
return View(resources);
}
[HttpPost]
public IActionResult Index(Resource resource)
{
return RedirectToAction("Index");
}
Test result:
I'm looping through a list to populate my questionnaire with selects. I haven't found any tutorial explaining how to extract values from multiple selects in one click. Any ideas?
Here's the view:
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#foreach (var question in Model.QuestionList)
{
<p>#question.Query</p>
<select asp-for="Answer">
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Here's the .cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using myquiz.Models;
using myquiz.Services;
namespace myquiz.Pages
{
public class QuizModel : PageModel
{
[ViewData]
[BindProperty]
public string Name { get; set; }
[BindProperty]
public Visitor Visitor { get; set; }
[BindProperty]
public List<Question> QuestionList { get; set; }
public string Answer { get; set; }
public void OnGet()
{
///QuestionList = new List<Question>();
// if (QuestionList is null)
// QuestionList = new List<Question>();
}
public void OnPost()
{
Name = Visitor.Name;
var quizService = new QuizService();
QuestionList = quizService.GetQuestions();
}
public void OnPostAnswer() {
Console.WriteLine("Yay, Answer works");
}
}
}
Here's the model
namespace myquiz.Models
{
public class Question
{
public int Id { get; set; }
public string Query { get; set; }
public string Option1 { get; set; }
public string Option2 { get; set; }
public string Option3 { get; set; }
public string Answer { get; set; }
}
}
Here are two ways to get the select answers:
The first way
All the select lists have the same name Answer,so you could receive an array named Answer in the backend and use BindProperty to bind the values:
public class IndexModel : PageModel
{
[ViewData]
[BindProperty]
public string Name { get; set; }
[BindProperty]
public List<Question1> QuestionList { get; set; }
[BindProperty]
public string[] Answer { get; set; } //modify this...
public void OnPostAnswer()
{
Console.WriteLine("Yay, Answer works");
}
}
View(be the same as yours):
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#foreach (var question in Model.QuestionList)
{
<p>#question.Query</p>
<select asp-for="Answer">
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Result:
The second way:
If you want to get the question id with selected answer in QuestionList,you need learn how does model binding system work firstly:
For each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.For QuestionList is a List,you need give the name like:QuestionList[index].Answer.
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#{
int i = 0;
} //add this...
#foreach (var question in Model.QuestionList)
{
<input hidden asp-for="QuestionList[i].Id" /> //add this..
<p>#question.Query</p>
<select asp-for="QuestionList[i].Answer"> //change asp-for
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
i++; //add this...
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Result:
Update:
Change <select asp-for="Answer"> to:
<select name="Answer">
//..
</select>
Result:
I created a search bar like this
<form method="post" asp-action="Search">
<input type="text" name="search" placeholder="Enter here the Name " />
<select name="type" id="type" class="form-control">
<option value="Success">Inactive Reservation</option>
<option value="Approved">Active Reservation</option>
<option value="Pending">Pending Reservation</option>
</select>
<input type="submit" value="Search" />
</form>
and the method in controller:
public async Task<IActionResult> Search(string search,string type)
{
var allRsv = from m in _db.Reservation
select m;
var Rsv = allRsv
.Where(x => x.ClientName.ToLower().Contains(search.ToLower()) &&
x.Status.ToLower().Contains(type.ToLower()));
return View(Rsv);
}
What I want: to send in search page something like 'You searched for #search and type: #type.
return View has no option to do this ,neither return to action ..
Can I do it in a simple way ?
My single idea it is to send query string and then request query in search view
What I want: to send in search page something like 'You searched for #search and type: #type.
You can try to pass data to search page via ViewData etc, like below.
In View Page
<form method="post" asp-action="Search">
<input type="text" name="search" placeholder="Enter here the Name " />
<select name="type" id="type" class="form-control">
<option value="Success">Inactive Reservation</option>
<option value="Approved">Active Reservation</option>
<option value="Pending">Pending Reservation</option>
</select>
<input type="submit" value="Search" />
</form>
<h3>You searched for "#ViewData["search"]" and type: #ViewData["type"].</h3>
In Action Method
public async Task<IActionResult> Search(string search, string type)
{
var allRsv = from m in _db.Reservation
select m;
var Rsv = allRsv
.Where(x => x.ClientName.ToLower().Contains(search.ToLower()) &&
x.Status.ToLower().Contains(type.ToLower()));
ViewData["search"] = search;
ViewData["type"] = type;
return View(Rsv);
}
Test Result
Using ViewData to pass data between controllers and views is fine as long as there are not many pieces of data in between. If you have lots of going, it will make everybody else harder to understand what's going on with ViewData because it's a weak-typed and you have no idea what it contains, what's available to get, etc. And then you have to go back to the controller and see what's being passed. What if there are multiple controllers returning this same view...yucky!
In addition, it's not a good practice to display what you have from your database directly from the controller to the view.
Hence you should use one of the alternatives of ViewData, that is ViewModel, which is strongly-typed!
Fake Entity Models
Since I don't have your database, for this demo, I am setting up two fake entity models that represent the data from your persistence storage.
namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
public class FakeReservationStatusEntity
{
public string StatusId { get; set; }
public string Status { get; set; }
}
public class FakeReservationEntity
{
public int ReservationId { get; set; }
public int ClientId { get; set; }
public string ClientName { get; set; }
public DateTime StartTimeUtc { get; set; }
public FakeReservationStatusEntity ReservationStatus { get; set; }
public int CreatedByUserId { get; set; }
}
}
There is one-to-many relationship between reservation and reservation status I assumed. And please notice I purposely made it so that it has more properties than the view model!
Viewmodels
They're just POCOs (Plain Old CLR Objects) that serve as data containers to travel between controllers and views.
namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
public class ReservationFiltersViewModel
{
[Display(Name = "Client name")]
public string ClientNameSearchQuery { get; set; }
[Display(Name = "Reservation type")]
public string ReservationTypeSearchQuery { get; set; }
public IDictionary<string, string> AvailableReservationTypes { get; set; }
}
public class ReservationViewModel
{
public int ReservationId { get; set; }
public string ReservationType { get; set; }
public string ClientName { get; set; }
public DateTime StartTime { get; set; }
}
public class ReservationListViewModel
{
public ReservationFiltersViewModel Filters { get; set; }
public IEnumerable<ReservationViewModel> Reservations { get; set; }
}
}
Controller
namespace DL.NetCore.EmptySolution.Web.UI.Controllers
{
public class ReservationController : Controller
{
public IActionResult Index(string c, string t)
{
var vm = new ReservationListViewModel
{
Filters = new ReservationFiltersViewModel
{
ClientNameSearchQuery = c,
ReservationTypeSearchQuery = t,
// You would normally get the list from your database
AvailableReservationTypes = GetFakeReservationStatusesFromDb()
.ToDictionary(x => x.StatusId, x => x.Status)
},
Reservations = Enumerable.Empty<ReservationViewModel>()
};
// You would normally get the list of reservations from your database
var reservationsFromDb = GetFakeReservationsFromDb();
// Filters
if (!String.IsNullOrWhiteSpace(c))
{
reservationsFromDb = reservationsFromDb
.Where(x => x.ClientName.Contains(c, StringComparison.InvariantCultureIgnoreCase));
}
if (!String.IsNullOrWhiteSpace(t))
{
reservationsFromDb = reservationsFromDb
.Where(x => x.ReservationStatus.StatusId.Contains(t, StringComparison.InvariantCultureIgnoreCase));
}
// See you only want to explore what you want on the view
vm.Reservations = reservationsFromDb
.Select(x => new ReservationViewModel
{
ReservationId = x.ReservationId,
ClientName = x.ClientName,
ReservationType = x.ReservationStatus.Status,
StartTime = x.StartTimeUtc.ToLocalTime()
});
return View(vm);
}
[HttpPost]
public IActionResult Search(ReservationFiltersViewModel filters)
{
return RedirectToAction(nameof(Index),
new { c = filters.ClientNameSearchQuery, t = filters.ReservationTypeSearchQuery });
}
...
}
}
Index View
#model DL.NetCore.EmptySolution.Web.UI.Models.Reservation.ReservationListViewModel
#{
ViewBag.Title = "Reservations";
var selectList = new SelectList(Model.Filters.AvailableReservationTypes, "Key", "Value");
}
<h2>Reservations</h2>
<p class="text-muted">
List of reservations you can manage
</p>
<div class="row">
<div class="col-lg-4">
<div class="card">
<div class="card-body">
<form method="post" asp-area="" asp-controller="reservation" asp-action="search">
<div class="form-group">
<label asp-for="Filters.ClientNameSearchQuery"></label>
<input asp-for="Filters.ClientNameSearchQuery" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Filters.ReservationTypeSearchQuery"></label>
<select asp-for="Filters.ReservationTypeSearchQuery" class="form-control"
asp-items="selectList">
<option value="">- select -</option>
</select>
</div>
<button type="submit" class="btn btn-success">Search</button>
</form>
</div>
</div>
</div>
<div class="col-lg-8">
<!-- This could be better optimized, i.e., only display non-empty search -->
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
You searched for <strong>#Model.Filters.ClientNameSearchQuery</strong>
and <strong>#Model.Filters.ReservationTypeSearchQuery</strong>
</div>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Client name</th>
<th>Start from</th>
<th>Type</th>
</tr>
</thead>
<tbody>
#foreach (var reservation in Model.Reservations)
{
<tr>
<td>#reservation.ReservationId</td>
<td>#reservation.ClientName</td>
<td>#reservation.StartTime.ToShortDateString()</td>
<td>#reservation.ReservationType</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
With ViewModel, there is no magic string flowing around like with ViewData. Everything is strongly typed. And the form only posts back the filter model that contains only what we need to the server.
Screenshots
Source code
My demo project's source code is at https://github.com/davidliang2008/DL.NetCore.EmptySolution. The checkin specifically for this demo is at https://github.com/davidliang2008/DL.NetCore.EmptySolution/commit/32087b989de06e316cf747ad49da6ad4b24b61b8
the ModelState is Invalid.I Think I pass Model Wrongly.Any Idea?
controller:
[Authorize]
[HttpPost]
public async Task<IActionResult> SendComment([Bind("CommentID,Comment,Date,AdminId")]AdminReport adminReport,int ReportID)
{
var x = _userReport.UserReports.Find(ReportID);
x.IsViewed = true;
adminReport.UserId = x.UserId;
adminReport.AdminId = _userManager.GetUserId(HttpContext.User);
if (ModelState.IsValid){
_adminReport.Add(adminReport);
await _adminReport.SaveChangesAsync();
return View(); }
return RedirectToAction("SendDoneAdmin");
}
its how I pass a Model:
<div class="card-footer">
<form asp-controller="Admin" asp-action="ΩSendComment" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>
Model:
[Key]
public int CommentID { get; set; }
[Required]
public string Comment { get; set; }
public string AdminId { get; set; }
public string UserId { get; set; }
}`
Your quotation no clear but must know for pass data via Form tag
must all input inside Form tag
Controller
public async Task<IActionResult> SendComment()
{
// write your code....
return View(new AdminReport()); // must return new object
}
POST
Normal write again your action SendComment no any change
HTML
for pass AdminReport model must write flied inside form tag
<form asp-controller="Admin" asp-action="Viewed" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
/* for example */
<input type="hidden" asp-for="model.Comment" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>
No Problem if you have another view but must return View(new AdminReport());
If you want remove validation for comment prop use below code.
// remove all key
foreach (var key in ModelState.Keys).ToList())
ModelState.Remove(key);
// or for one
ModelState.Remove("comment ");
You put this [Requiried] attribute on the Comment:
[Required]
public string Comment { get; set; }
You will have to include that input in your form in order for the validation to pass.
You can add that field like this:
<div class="card-footer">
<form asp-controller="Admin" asp-action="Viewed" method="post">
<input type="hidden" value="#report.ReportID" name="ReportID" />
<input type="text" name="Comment" />
<button type="submit" class="btn btn-primary">SendComment</button>
</form>