how to bind data to checkbox from viewmodel - asp.net-mvc-4

I'm trying to make a view model to show a list of checkboxes. A checkbox will be checked when its ID is found in the database. However, my code is generating an error.
CheckFacilityVN
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace XNet.WebUI.Hotel.ViewModel
{
public class CheckFacilityVM
{
public int FacilityID { get; set; }
public string facilityName { get; set; }
public List<FacilityAvailable> facilityAvailable { get; set; }
}
public class FacilityAvailable
{
public bool isCheck { get; set; }
}
}
My controller
public ActionResult Facility()
{
var htl = _hotelService.ShowRoomFacility(2);
var list = new List<FacilityAvailable>();
foreach (var x in htl)
{
list.Add(new FacilityAvailable { FacilityID = htl.FacilityID, facilityName = htl.FacilityName, isCheck = htl.IsActive });
}
return View();
}
My constructor
public Facility ShowRoomFacility(int HotelID)
{
var x = (from d in db.Facilities
where d.FacilityID == HotelID
select d).FirstOrDefault();
return x;
}
How can I make these checkboxes?

Start by adapting your view model:
public class CheckFacilityVM
{
public int FacilityID { get; set; }
public string FacilityName { get; set; }
public bool IsFacilityAvailable { get; set; }
}
and then use this view model:
public ActionResult Facility()
{
var model = _hotelService
.ShowRoomFacility(2)
.Select(htl => new CheckFacilityVM
{
FacilityID = html.FacilityID,
FacilityName = html.FacilityName,
IsFacilityAvailable = htl.IsActive,
})
.ToList();
return View(model);
}
and then write a corresponding view:
#model List<CheckFacilityVM>
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Is available</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x[i].FacilityID)
#Html.HiddenFor(x => x[i].FacilityID)
</td>
<td>
#Html.DisplayFor(x => x[i].FacilityName)
#Html.HiddenFor(x => x[i].FacilityName)
</td>
<td>
#Html.CheckBoxFor(x => x[i].IsFacilityAvailable)
</td>
</tr>
}
</tbody>
</table>
<button type="submit">Save</button>
}
and finally:
[HttpPost]
public ActionResult Facility(List<CheckFacilityVM> model)
{
// process the values from the view model here ...
}

Related

How can I dynamically update the span and the contents of the first row of headers in a table with 2 rows of headers in ASP.Net Core 3.1?

I'm trying to make a table with 2 rows of headers, both of them update dynamically from 2 separate database tables. The content update part is ok, it works. The problem is, the cells in the first row must span over whatever the number of entries of that particular category is in the 2nd row.
I've grouped the second row categories by elements with their id, counted the ids and put them in a ViewBag.
The first row model:
[Table("categories")]
public class CategoriesModel
{
[Column("category_id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
public int CategoryId { get; set; }
[Column("category_name")]
//[Required]
public string CategoryName { get; set; }
}
Second row model:
[Table("materials")]
public class MaterialModel
{
[Column("material_id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
public int MaterialId { get; set; }
[Column("height_x_width")]
[Required]
public string Dimensions { get; set; }
[Column("category_id")]
[Required]
public int CategoryId { get; set; }
public List<CategoriesModel> MaterialCategory { get; set; }
}
The ViewModel:
public class NationalCampaignViewModel
{
public string Campaign { get; set; }
public IEnumerable<RestaurantModel> Restaurant { get; set; }
public IEnumerable<CategoriesModel> Categories { get; set; }
public IEnumerable<MaterialModel> Materials { get; set; }
public IEnumerable<OptionModel> Options { get; set; }
}
the ViewModelController:
public class NationalCampaignViewModelController : Controller
{
private readonly McDdbContext _context;
public NationalCampaignViewModelController(McDdbContext context)
{
_context = context;
}
// GET: NationalCampaign
public async Task<IActionResult> Index(int? id)
{
var campaign = await _context.NationalCampaigns.FirstOrDefaultAsync(m => m.Id == id);
var restaurant = await _context.Restaurants.ToListAsync();
//var stl = await _context.STL.ToListAsync();
//var dtl = await _context.DTL.ToListAsync();
var categories = await _context.CategoriesModel.ToListAsync();
var options = await _context.OptionModel.ToListAsync();
var materials = await _context.MaterialModel.ToListAsync();
var colSpan = materials.OrderBy(x => x.CategoryId).GroupBy(x => x.CategoryId).ToDictionary(g => g.Key, g => g.Count());
ViewBag.ColSpan = colSpan.Values;
var tables = new NationalCampaignViewModel
{
Campaign = campaign.CampaignName,
Restaurant = from r in restaurant select r,
Categories = from c in categories orderby c.CategoryId select c,
//Options = from o in options select o,
Materials = from m in materials orderby m.CategoryId select m
};
if (id == null)
{
return NotFound();
}
return View(tables);
}
The View:
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>#Model.Campaign</h1>
<div class="container">
<div class="row">
<div class="col">
<table class="table-bordered">
#foreach (var span in ViewBag.ColSpan)
{
<colgroup>
<col span="#span" />
</colgroup>
}
<thead>
<tr align="center">
<th>#Html.DisplayNameFor(model => model.Restaurant.FirstOrDefault().Restaurant_Name)</th>
#foreach (var c in Model.Categories)
{
<th scope="colgroup">#c.CategoryName</th>
}
</tr>
<tr>
<th>Dimensions</th>
#foreach (var m in Model.Materials)
{
<th>#m.Dimensions</th>
}
</tr>
</thead>
<tbody>
#*#foreach (var item in Model.Restaurant)
{
<tr>
<td>
<a asp-controller="Restaurant" asp-action="Edit"
asp-route-id="#item.Restaurant_Id">#Html.DisplayFor(modelItem => item.Restaurant_Name)</a>
</td>
</tr>
}*#
</tbody>
</table>
</div>
The table with the number of columns to span over:
Solved it with dictionary.
Controller:
var colSpan = materials.OrderBy(x => x.CategoryId).GroupBy(x => x.CategoryId).ToDictionary(g => g.Key, g => g.Count());
Dictionary<string, int> headerValues = new Dictionary<string, int>();
List<string> keys = new List<string>();
foreach (var category in categories)
{
keys.Add(category.CategoryName);
}
headerValues = keys.Zip(colSpan.Values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
ViewBag.ColSpan = headerValues;
View:
#foreach (var item in ViewBag.Colspan)
{
<th colspan="#item.Value">#item.Key</th>
}

Asp Core Filter & Search Data Using Drop Downs & Search Bars

I would like users to search through pets using a search bar or drop down lists. I tried using viewbag and asp tag helpers but I keep getting errors. Below is a picture of what i'm going for. Any help is appreciated.
Model
public class Reptile
{
public int ReptileId { get; set; }
public string Name { get; set; }
public string Age { get; set; }
[Display(Name ="Reptile's Image")]
public byte[] Image { get; set; }
[Display(Name ="Food Requirements")]
public string FoodReq { get; set; }
[Display(Name="Habitat Requiremtns")]
public string HabitatReq { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
public string Size { get; set; }
public string Color { get; set; }
[Display(Name="Recent Checkup")]
public bool RecentCheckup { get; set; }
public bool Trained { get; set; }
public bool Neutered { get; set; }
public bool Declawed { get; set; }
[Display(Name = "Good With Other Reptiles")]
public bool GoodWithRept { get; set; }
[Display(Name = "Good With Kids")]
public bool GoodWithKids { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int ApplicationUserId { get; set; }
}
Controller
public async Task<IActionResult> Index(string searchString)
{
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
return View(await reptiles.ToListAsync());
}
View
<form asp-controller="Reptiles" asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Search By Type: <input type="text" name="SearchString" />
<input type="submit" value="Filter" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
I've been trying to follow the docs here Tutorial: Add sorting, filtering, and paging - ASP.NET MVC with EF Core. Not having any luck though.
Here is a simple demo to show how to use searchstring:
Controller:
public IActionResult Index(string searchString)
{
IEnumerable<Reptile> list = new List<Reptile> { new Reptile { Type = "t1", Name= "Reptile1" }, new Reptile { Type = "t2", Name = "Reptile2" }, new Reptile { Type = "t3", Name = "Reptile3" } };
ViewData["CurrentFilter"] = searchString;
if (!String.IsNullOrEmpty(searchString))
{
list = list.Where(s => s.Name.Contains(searchString));
}
return View(list);
}
View:
Find by name:
|
Back to Full List
<table>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<input type="text" asp-for="#item.Type">
</td>
</tr>
}
</tbody>
</table>
result:
Okay, I figured out how to use select to filter the reptile page by using the data users already added to the database from the properties in the model. I had to create a view model and add the Reptile model to it.
View Model
public class ReptileGenderViewModel
{
public Reptile Reptile { get; set; }
public List<Reptile> reptiles;
public SelectList genders;
public string reptileGender { get; set; }
}
Reptile Controller
public async Task<IActionResult> Index(string searchString, string reptileGender)
{
IQueryable<string> genderQuery = from g in _context.Reptiles
orderby g.Gender
select g.Gender;
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
if (!string.IsNullOrEmpty(reptileGender))
{
reptiles = reptiles.Where(g => g.Gender == reptileGender);
}
var reptileGenderVM = new ReptileGenderViewModel();
reptileGenderVM.genders = new SelectList(await genderQuery.Distinct().ToListAsync());
reptileGenderVM.reptiles = await reptiles.ToListAsync();
return View(reptileGenderVM);
}
View
<select asp-for="reptileGender" asp-items="Model.genders">
<option value="">All</option>
</select>

How to deal with this decimal error for a price?

I'm working on this app that should show on "localhost/catalog" some data. I have a library for the models and for the services that the application might use. I am getting this error:
InvalidOperationException: The property 'Price' is not a navigation
property of entity type 'StoreAsset'. The 'Include(string)' method can
only be used with a '.' separated list of navigation property names.Microsoft.EntityFrameworkCore.Query.Internal.IncludeCompiler.WalkNavigations(IEntityType entityType, IReadOnlyList<string> navigationPropertyPaths, IncludeLoadTree includeLoadTree, bool shouldThrow)
Here is the code that I'm using (controller, models and view) and the service methods on bottom:
public class CatalogController : Controller
{
private IStoreAsset _assets;
public CatalogController(IStoreAsset assets)
{
_assets = assets;
}
public ActionResult Index()
{
var assetModels = _assets.GetAll();
var listingResult = assetModels
.Select(result => new AssetIndexListingModel
{
Id = result.Id,
Tipology = _assets.GetTipology(result.Id),
Size = _assets.GetSize(result.Id),
Price = decimal.Parse(_assets.GetPrice(result.Id))
});
var model = new AssetIndexModel()
{
Assets = listingResult
};
return View(model);
}
public class AssetIndexListingModel
{
public int Id { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
public string Tipology { get; set; }
public string ImageUrl { get; set; }
}
public abstract class StoreAsset
{
public int Id { get; set; }
[Required]
public Status Status { get; set; }
[Required]
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
public class Dress : StoreAsset
{
[Required]
public string Color { get; set; }
[Required]
public string Tipology { get; set; }
[Required]
public string Size { get; set; }
}
#model Models.Catalog.AssetIndexModel
<div id="assets">
<h3></h3>
<div id="assetsTable">
<table class="table table-condensed" id="catalogIndexTable">
<thead>
<tr>
<th>Size</th>
<th>Price</th>
<th>Tipology</th>
</tr>
</thead>
<tbody>
#foreach (var asset in Model.Assets)
{
<tr class="assetRow">
<td class="">
<a asp-controller="Catalog" asp-action="Detail" asp-route-id="#asset.Id">
<img src="#asset.ImageUrl" class="imageCell" />
</a>
</td>
<td class="">#asset.Price</td>
<td class="">#asset.Size</td>
<td class="">#asset.Tipology</td>
</tr>
}
</tbody>
</table>
</div>
public class StoreAssetService : IStoreAsset
{
private Context _context;
public StoreAssetService(Context context)
{
_context = context;
}
public void Add(StoreAsset newAsset)
{
_context.Add(newAsset);
_context.SaveChanges();
}
public IEnumerable<StoreAsset> GetAll()
{
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
}
public StoreAsset GetById(int id)
{
// Return a query (same as returning GetAll().FirstOrDefault(...))
return _context.StoreAssets
.Include(assets => assets.Status)
.Include(assets => assets.Price)
// So it can return null with no problem
.FirstOrDefault(asset => asset.Id == id);
}
public StoreBranch GetCurrentLocation(int id)
{
throw new NotImplementedException();
}
// To implement and test
public string GetPrice(int id)
{
return _context.Dresses.FirstOrDefault(p => p.Id == id).Price.ToString();
}
public string GetSize(int id)
{
return _context.Dresses.FirstOrDefault(s => s.Id == id).Size;
}
public string GetStatus(int id)
{
throw new NotImplementedException();
}
public string GetTipology(int id)
{
var dress = _context.StoreAssets.OfType<Dress>()
.Where(b => b.Id == id);
// For now return other if it's not a party dress
return dress.Any() ? "Party" : "Other";
}
}
Should I use some ForeignKey attribute or change Price to a string?
Any help would be great thanks
As pointed out in the error message, the Include is for the Navigation property only.
You need to change below:
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
To:
return _context.StoreAssets
.Include(asset => asset.Status).ToList();
Reference: https://learn.microsoft.com/en-us/ef/core/modeling/relationships#definition-of-terms
https://learn.microsoft.com/en-us/ef/core/querying/related-data
I am having yet another problem. When I go to "localhost/catalog" the page should display all columns/entries that I have in the database but it only displays one column. Is there something wrong in the foreach cicle?

Data column into db MVC4

<table>
#foreach (DataRow row in Model.Rows)
{
<tr>
#foreach (DataColumn col in Model.Columns)
{
<td>#row[col.ColumnName]</td>
}
</tr>
}
</table>
#Html.ActionLink("Pass", "Insert", "Home")
When clicking the pass link i would like the columns to be inserted into db. Is there any tutorial on how to do this?
Please try this sample.
Create a view model class as follows
public class DataRows
{
public int Column1{ get; set; }
public string Column2 { get; set; }
public double Column3 { get; set; }
}
public class MyModel
{
public virtual DataRows[] DataSet { get; set; }
}
Retrieve all the data using your business logic into MyModel in your controller. I am demonstrating this using a sample code
public TestModel GenerateModel()
{
var model = new TestModel();
model.DataSet = this.GenerateDataGridData().ToArray();
return model;
}
private ICollection<SampleDataSet> GenerateDataGridData()
{
var list = new List<SampleDataSet>()
{
new SampleDataSet() { Column1= 1, Column2= "XXX", Column3= 23 },
new SampleDataSet() { Column1= 2, Column2= "YYY", Column3= 27 },
new SampleDataSet() { Column1= 3, Column2= "ZZ", Column3= 25 }
};
return list;
}
In the Action of your view you will be calling GenerateModel method as follows
public ActionResult MyView()
{
var model = this.GenerateModel();
return this.View(model);
}
Now you restructure your MyView page as follows
#model MyModel
#{
int i = 0;
}
#using (Html.BeginForm("Insert", "Home", FormMethod.Post))
{
<table>
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</tr>
</thead>
<tbody>
#foreach (var row in Model.DataSet)
{
<tr>
<th>#row.Column1<span>#Html.HiddenFor(x => Model.DataSet[i].Column1)</span></th>
<th>#row.Column2<span>#Html.HiddenFor(x => Model.DataSet[i].Column2)</span></th>
<th>#row.Column3<span>#Html.HiddenFor(x => Model.DataSet[i].Column3)</span></th>
</tr>
i++;
}
</tbody>
</table>
<input type="submit" value="Pass" />
}
Now on submiting the data in the view will be posted to your Insert Action of Home controller and you can insert into your DB using your logic
I hope this will be a solution for your query.

MVC4 ViewModel to View from database - Example

Keep banging my head over seamlessly simple problem. Can somebody write me complete code for:
2 models:
public class State
{
public int StateID { get; set; }
public string Name { get; set; }
}
public class City
{
public int CityID { get; set; }
public string Name { get; set; }
public int StateID { get; set; }
[ForeignKey("StateID")]
public State State { get; set; }
public IEnumerable<SelectListItem> StateList { get; set; }
}
Problem is the rest. How to write ViewModel, Index ActionResult and View? Am using EF within built in testing environment.
I simply run out of combinations to try :(
#
And here is my final solution that works:
ViewModel
public class CityIndexViewModel
{
public int CityID { get; set; }
public string CityName { get; set; }
public int StateId { get; set; }
public string StateName { get; set; }
}
Controller
public ActionResult Index()
{
var model = new List<CityIndexViewModel>();
var dbCity = db.City.Include("State");
foreach (City c in dbCity)
{
var cityIndex = new CityIndexViewModel()
{
CityID = c.CityID,
CityName = c.Name,
StateId = c.State.StateID,
StateName = c.State.Name
};
model.Add(cityIndex);
}
return View(model);
}
View
#model IEnumerable<app.ViewModel.CityIndexViewModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Citys
#*#Html.DisplayNameFor(model => model.Citys)*#
</th>
<th>
States
#*#Html.DisplayNameFor(model => model.States)*#
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CityName)
</td>
<td>
#Html.DisplayFor(modelItem => item.StateName)
</td>
<td>
#* #Html.ActionLink("Edit", "Edit", new { id=item.CityID }) |
#Html.ActionLink("Details", "Details", new { id=item.CityID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CityID })*#
</td>
</tr>
}
</table>