I have tried to create dropdown list with tag
<select name="manufacturer" class="form-control">
<option>#null</option>
#foreach (var item in Model) {
<option>#item.Manufacturer</option>
}
</select><br>
but here is a problem: It doesn't populate all available manufacturers and only shows items, from current page. Example: http://i.imgur.com/na0x5eQ.png
that's because it uses the model passed from controller which doesn't always contain every item from the database. (because of pagination or after search)
Here is full sidebar code in partial view
#model IPagedList<Products>
#using PagedList
#using PagedList.Mvc
<div id="filter" class="left">
#using (Html.BeginForm("Filter", "SearchFilter")) {
<div>
<b>Manufacturer:</b> <br>
<select name="manufacturer" class="form-control">
<option>#null</option>
#foreach (var item in Model) {
<option>#item.Manufacturer</option>
}
</select><br>
<b>Name:</b> <br>#Html.TextBox("name", null)<br>
<b>Price From:</b> <br>#Html.TextBox("min", null)<br>
<b>To:</b> <br>#Html.TextBox("max", null)<br>
<button type="submit" value="search"><b>Search</b></button>
</div>
}
</div>
So what are the ways to fix this?
Since you know why it's occurring
that's because it uses the model passed from controller which doesn't always contain every item from the database. (because of pagination or after search)
then why not fix it right there. Maybe you need a separate validation table for manufacturers (if you don't already have one).
Model:
public class ProductsModel
{
public IPagedList<Products> CurrentProducts {get; set;}
public int ManufacturerId {get; set;}
public int MinPrice{get; set;}
public int MaxPrice{get; set;}
}
Controller action:
[HttpGet]
public ViewResult CurrentProducts()
{
ProductsModel model = new ProductsModel();
model.CurrentProducts = _repository.Product;
ViewBag.ManufacturerId = from p in _repository.Product select p.Manufacturer;
return View(model);
}
[HttpPost]
public ViewResult CurrentProducts(ProductsModel model)
{
if(ModelState.IsValid)
{
model.CurrentProducts = _repository.Product.Where(t=>t.ManufacturerId == model.ManufacturerId);
ViewBag.ManufacturerId = from p in _repository.Product select p.Manufacturer;
}
return View(model);
}
And in the view, create a dropdown list like this:
#model MyNameSpace.ProductsModel
#Html.DropDownListFor(model=> model.ManufacturerId, string.Empty);
Related
I am building my first web app with .net core razor pages.
I am trying to move my menu to be partial, so I can reuse it as I wish but I must be doing something wrong.
The error is:
The model item passed into the ViewDataDictionary is of type 'BrambiShop.UI.Pages.IndexModel', but this ViewDataDictionary instance requires a model item of type 'BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel'.
I know there are few topics on that, but I am struggling " days after work now and I couldnt find any exact solution. I quess I kinda understand what I am doing wrong. But I dont know how to do it right.
I am trying to load the partial from index as follows:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
#{ await Html.RenderPartialAsync("_SideCategoriesMenu"); }
The partial is:
#page
#model BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel
#*
For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*#
#foreach (var category in Model.Categories)
{
<button class="font-weight-bold" data-toggle="collapse" href="#MenuCollapse_#Html.DisplayFor(modelItem => category.Id)"
aria-expanded="false" aria-controls="MenuCollapse_#Html.DisplayFor(modelItem => category.Id)">
#Html.DisplayFor(modelItem => category.Name)
</button>
<!-- Collapsible element -->
<div class="collapse text-center" id="MenuCollapse_#Html.DisplayFor(modelItem => category.Id)">
#foreach (var subCategory in Model.SubCategories.Where(x => x.CategoryId == category.Id))
{
<form action="/url/to/action" Method="GET">
<input type="hidden" name="Property" value="#Html.DisplayFor(modelItem => subCategory.Id)" />
<button type="submit">
<i class="fas fa-caret-right pl-1"></i>#Html.DisplayFor(modelItem => subCategory.Name)
</button>
</form>
}
</div>
<!-- / Collapsible element -->
}
And at last the cshtml.cs
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages.Shared
{
public class _SideCategoriesMenuModel : PageModel
{
private readonly IApiClient _Client;
public _SideCategoriesMenuModel(IApiClient client)
{
_Client = client;
}
public IList<Category> Categories { get; set; }
public IList<SubCategory> SubCategories { get; set; }
public async Task OnGet()
{
Categories = await _Client.GetCategoriesAsync();
SubCategories = await _Client.GetSubCategoriesAsync();
}
}
}
Can someone help me with that please? I did watch about 10 hours of tutorials to build api and the ground of the web, but this damn partial. I just cant find answer to that anywhere.
Thank you very much for any effort.
EDIT
--- index model
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages
{
public class IndexModel : PageModel
{
public IndexModel()
{
}
public async Task OnGet()
{
}
}
}
Your Index view has a model of type IndexModel. When you put this line
#{ await Html.RenderPartialAsync("_SideCategoriesMenu"); }
The same model will be passed to _SideCategoriesMenu which is IndexModel. This is why you are receiving the exception because the model passed to the _SideCategoriesMenu is different since it expects BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel.
To go around that, you have to specify the model in the RenderPartialAsync method. ex:
#{ await Html.RenderPartialAsync("_SideCategoriesMenu",YOUR_MODEL_HERE); }
You can replace YOUR_MODEL_HERE with whatever instance of type _SideCategoriesMenuModel. You can have it as a property inside IndexModel. ex:
public class IndexModel : PageModel
{
public IndexModel()
{
}
public SideMenuModel SideMenu { get; set;}
public BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel SideMenuModel{ get; set;}
public async Task OnGet()
{
}
}
}
And then use it in your index view
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
#{ await Html.RenderPartialAsync("_SideCategoriesMenu",Model.SideMenuModel); }
The model you use to create the page needs to contain a complete model for the Menu. Then you need to pass the model in the RenderPartial.
IE:
#{ await Html.RenderPartialAsync("_SideCategoriesMenu", Model.SideMenu); }
Edit--
Add the SideMenu property to your Model so you can access it on the page in the above example.
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages
{
public class IndexModel : PageModel
{
public IndexModel()
{
}
public SideMenuModel SideMenu { get; set;}
public async Task OnGet()
{
}
}
}
I'm just looking for a better way to do the following :
I've got an html select :
<form method="post" action="/Account/ChangeUserRole">
<select name="Val" onchange="this.form.submit();" class="span2">
#foreach (var r in ViewBag.UserRoles)
{
#if (u.UserRole.ID == r.ID)
{
<option selected="selected" value="#u.ID/#r.ID">#r.Name</option>
}
else
{
<option value="#u.ID/#r.ID">#r.Name</option> // <-- better way?
}
}
</select>
</form>
I'm posting it as "userid/roleid" and on the controller side doing a string.Split on / to split u.ID and r.ID
I would like to know if it's possible to post it so my controller get's them in this way :
[HttpPost]
public IActionResult ChangeUserRole(int UserID, int RoleID)
Instead of this witchcraft:
[HttpPost]
public IActionResult ChangeUserRole(string Val)
{
char[] splitChar = new char[] { '/' };
string[] s = Val.Split(splitChar);
int UserID = Convert.ToInt32(s[0]);
int RoleID = Convert.ToInt32(s[1]);
}
Sorry for the long post. Hope my question makes sense.
I'm not such a big fan of html helpers.
Side note:
I'm using MVC 6, ASP 5 - RC1
Appreciate the help
Cheers!
The best solution is to use the TagHelpers to build your dropdown. Let's start by creating a view model specific to this view.
public class UserRoleEditVm
{
public List<SelectListItem> Roles { set; get; }
public int RoleId { set; get; }
public int UserId { set; get; }
}
In your get action, create an object of this, load the property values and send it to the view.
public IActionResult Create()
{
// User Id and Role list is hard coded for demo. You may replace it with real data.
var v = new UserRoleEditVm {UserId = 45};
v.Roles = new List<SelectListItem>
{
new SelectListItem {Value = "1", Text = "Admin"},
new SelectListItem {Value = "2", Text = "Editor"},
new SelectListItem {Value = "3", Text = "Reader"}
};
return View(v);
}
And in your view, which is strongly typed to our view model, we will like Tag helpers to for creating the HTML markup.
#model UserRoleEditVm
<form asp-action="ChangeUserRole" asp-controller="Account">
<select asp-for="RoleId" asp-items="#Model.Roles">
<option>Please select one role</option>
</select>
<input type="hidden"asp-for="UserId"/>
<input type="submit"/>
</form>
And in your HttpPost action method, you can use an object of our view model as the parameter and the Model binder will map the posted form values to property values of that object.
[HttpPost]
public ActionResult ChangeUserRole(UserRoleEditVm model)
{
var userId = model.UserId;
var roleId = model.RoleId;
// to do : Do something with the above 2 values
// to do :Save and redirect (PRG pattern)
// return RedirectToAction("Success");
}
I'm attempting to make a simple page that will compare multiple form submissions.
I have a html page with a form, and a for-loop that generates a div for each item in a list of form submissions. The list is passed from the controller. I am trying to maintain the list in the controller rather than rely on a database.
When I try to resubmit the form, which should add another object to the list, the list re initializes.
In debugging, I see that the list is empty when the form gets submitted. I'm unsure as to the correct terminology, but it seems that the list is emptied whenever the view is rendered. Is there a way to maintain list contents?
I know there are better ways to do this, and welcome any advice. I'm still learning, so pleas go easy.
Thanks!
This is the simplified controller.
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
List<paymentPlan> plansList = new List<paymentPlan>();
public ActionResult Index()
{
return View(plansList);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
plansList.Add(Project);
return View(plansList);
}
}
}
This is my simplified view.
#model List<MvcApplication2.Models.paymentPlan>
#using (Html.BeginForm("index", "home", FormMethod.Post, new { Id = "signupForm" }))
{
<label for="customerName">Customer Name:</label>
<input type="text" name="customerName" class="form-control required" />
#Html.ValidationSummary(true)
<input type="submit" value="Calculate" class="btn btn-primary" />
}
#{
bool isEmpty = !Model.Any();
if (!isEmpty)
{
foreach (var i in Model)
{
<div>
Name: #i.customerName
</div>
}
}
}
This is my simplified model.
namespace MvcApplication2.Models
{
public class paymentPlan
{
public string customerName { get; set; }
}
}
I think that's a question of controller and asp.Net MVC lifecycle !
A controller lifetime is the same as the request, for each request a new controller is created and once the work is done it's disposed!
So try to remove this List<paymentPlan> plansList = new List<paymentPlan>(); and work with TempData[] or ViewData[] or Session[] like this :
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
Session["plansList"] = ((List<paymentPlan>)Session["plansList"])!=null? (List<paymentPlan>)Session["plansList"] : new List<paymentPlan>();
return View((List<paymentPlan>)Session["plansList"]);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
((List<paymentPlan>)Session["plansList"]).Add(Project);
return View(plansList);
}
}
check this : http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application
I'm new in asp.
I must write an application in mvc which have a database where I have a two tables.
One table Parent is realted one to many with table Children.
Parent
-------------
parent_id
name
Children
-------------
children_id
parent_id (foreign key)
name
When user want to create an element for Parent table, the editor must have an opportunity to add(create) Children.
When user edit Parent then he should have opportunity to delete/edit Children.
There isn't possible to add/edit/delete Children in separete editor
I think I sholuld use a javascript to generate further controls for Child. I can do this but I don't know how to map this html controls to model?
I have wrote this view:
<script language="javascript" type="text/javascript">
function add_child() {
$('#children').append($('<input name="child[' + ++$('#children>input').get().length + ']" />'));
}
</script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Create Parent</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div>Children:</div>
<div id="children">
#Html.TextBox("child[1]")
</div>
<div>
<input onclick="add_child()" type="button" />
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
This works fine. But I relly don't know how to map this element on my Parent Model.
Now my Model for create view looks like this:
public class ParentModel {
public ParentModel() {
Children = //... get all children from database
}
public string Name { get; set; }
// this should be an ids of selected children
public string[] SelectedChildren { get; set; }
// children to display
public ICollection<ChildModel> Children { get; set; }
}
public class ChildModel {
public string Name { get; set; }
}
How to fill this Children by the values from view?
Controller:
public class ParentController : Controller
{
// show form
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index() {
ParentModel model = new ParentModel();
return View(model);
}
// Get Parent and Children
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(ParentModel form) {
// I want to read a form.CreatedChildren
// but it is Null
return View();
}
}
I want to read a form.CreatedChildren but it is Null...
You may checkout the following blog post. And the followup about validation.
If I understand your model correctly, you need
// children to display
public ICollection<ChildModel> Children { get; set; }
in your parent model. In your ParentModel constructor
Children = new HashSet<ChildModel>();
As far as populating the model with data, that belongs in your controller. Something like:
var parents = from p in context.ParentModels
select p;
Return View(parents);
If you have a select list set to multiple in ASP.NET MVC, how does the modelbinding work?
What does it return for your selected items, an array?
<SELECT NAME="toppings" MULTIPLE SIZE=5>
<option value="mushrooms">mushrooms</option>
<option value="greenpeppers">green peppers</option>
<option value="onions">onions</option>
<option value="tomatoes">tomatoes</option>
<option value="olives">olives</option>
</SELECT>
Yes, by default a multiselectlist will post through an array of the selected values.
This article has further information, including how to use strongly-typed views with a multiselectlist.
From the linked "article":
Your model or view model class needs a collection property for the IDs for the selected option items, e.g. List<int> ToppingIds.
In the controller action method to which the form containing your multi-select-list POSTs, you can access the selected option items thru the collection property you added to the model or view model class.
Yes, it returns an array.
View model:
public class MyViewModel
{
public int[] SelectedIds { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Controller:
public ActionResult Index()
{
var model = new MyViewModel
{
// fetch the items from some data source
Items = Enumerable.Select(x => new SelectListItem
{
Value = x.Id,
Text = "item " + x.Id
})
};
return View(model);
}
View:
#model MyViewModel
#Html.ListBoxFor(x => x.SelectedIds, Model.Items)
In VegTableViewmodel:
public IEnumerable<MultiSelectList> Vegetables { get; set; }
In the Controller:
Get vegetables list, and then pass it to the VegTableViewModel's Vegetables property.
viewmodel.Vegetables = vegetables .Select(d => new MultiSelectList(d.VegName));
In the View:
#Html.ListBoxFor(m => m.L, new MultiSelectList(Model.Vegetables.Select(d => d.Items))