Populating dropdownlist based on previous selected item asp.net mvc - c#

I am trying to populate a DropDownList based on the previous selected item. To achieve that, I have created three models
Country model:
[Key]
public int CountryId { get; set; }
public string CountryName { get; set; }
public virtual ICollection<State> States { get; set; }
State model:
[Key]
public int StateId { get; set; }
public string StateName { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<City> Citys { get; set; }
City model:
[Key]
public int CityId { get; set; }
public string CityName { get; set; }
[ForeignKey("State")]
public int StateId { get; set; }
public virtual State State { get; set; }
and here is my controller:
private ProjectContext db = new ProjectContext();
//
// GET: /CascadingDropdown/
public ActionResult Index()
{
ViewBag.CountryId = new SelectList(db.Countrys, "CountryId", "CountryName");
return View();
}
public JsonResult StateList(int Id)
{
var state = from s in db.States
where s.CountryId == Id
select s;
return Json(new SelectList(state.ToArray(), "StateId", "StateName"), JsonRequestBehavior.AllowGet);
}
public JsonResult Citylist(int id)
{
var city = from c in db.Citys
where c.StateId == id
select c;
return Json(new SelectList(city.ToArray(), "CityId", "CityName"), JsonRequestBehavior.AllowGet);
}
public IList<State> Getstate(int CountryId)
{
return db.States.Where(m => m.CountryId == CountryId).ToList();
}
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult LoadClassesByCountryId(string CountryName)
{
var stateList = this.Getstate(Convert.ToInt32(CountryName));
var stateData = stateList.Select(m => new SelectListItem()
{
Text = m.StateName,
Value = m.CountryId.ToString(),
});
return Json(stateData, JsonRequestBehavior.AllowGet);
}
And then my script:
$(function () {
$('#Country').change(function () {
$.getJSON('/Cascading/StateList/' + $('#Country').val(), function (data) {
var items = '<option>Select a State</option>';
$.each(data, function (i, state) {
items += "<option value='" + state.Value + "'>" + state.Text + "</option>";
});
$('#State').html(items);
});
});
$('#State').change(function () {
$.getJSON('/Cascading/Citylist/' + $('#State').val(), function (data) {
var items = '<option>Select a City</option>';
$.each(data, function (i, city) {
items += "<option value='" + city.Value + "'>" + city.Text + "</option>";
});
$('#city').html(items);
});
});
});
Finally I display it with this view:
#model Test_restriction.DAL.ProjectContext
#using (Html.BeginForm())
{
#Html.DropDownList("Country", ViewBag.CountryId as SelectList, "Select a Country", new { id="Country" })<br />
<select id="State" name="state"></select><br />
<select id="city" name="City"></select><br />
}
#section js
{
<script src="~/Scripts/Testing.js" type="text/javascript"></script>
}
So I prefer to post all my code to be clear, now problem is that only the first DropDownList with countries is filled the two others DropDownList remain empty. Can someone help to find what is going wrong?
Thank you!

Try This
View
#model Test_restriction.DAL.CountryModel
<script type="text/javscript">
$(function(){
$('#ddlcountry').change(function () {
var sub = $('#ddlstate').val();
if (sub != null && sub != "") {
$('#ddlstate').html(' <option value="">--Select Topic--</option>');
$.ajax({
url: '/Cascading/StateList',
data: { id: sub },
type: 'post',
success: function (data) {
if (data != null && data != "") {
$.each(data, function (i, item) {
$("#ddlstate").append($("<option></option>").val(item.value).html(item.text));
});
}
else {
$('#ddlstate').html(' <option value="">--Select State--</option>');
}
}
});
}
else {
$('#ddlstate').html(' <option value="">--Select State--</option>');
}
});
});
</script>
#Html.DropDownListFor(m => m.subject_id, (SelectList)ViewBag.Countries, "--Select Country--", new { #Class = "form-control", id = "ddlcountry" })
<select id="ddlstate" name="state_id" class="form-control">
<option value="">--Select State--</option>
</select>
Controller
public ActionResult Index()
{
ViewBag.countries = new SelectList(db.Countrys, "CountryId", "CountryName");
return View();
}
public JsonResult StateList(int Id)
{
var state = (from s in db.States
where s.CountryId == Id
select s).ToList();
var list = state.Select(m => new { value = m..StateId, text = m.stateName });
return Json(list, JsonRequestBehavior.AllowGet);
}
Use same method for city dropdown..

Related

How do I return option value from DropDownList into controller in ASP.NET

Here is the Razor code
#Html.DropDownList("ddl", Model.estados.Select(item => new SelectListItem
{
Value = item.Id_Estado.ToString(),
Text = item.Nombre_Estado,
Selected = "select" == item.Id_Estado.ToString()
}), new { #class = "form-select", aria_label="Default select example" }
)
Here is the view model, it's an IEnumerable:
public class ViewModel
{
public UsuariosViewModel usuario { get; set; }
public IEnumerable<TiposUsuariosViewModel> tiposUsuarios { get; set; }
public IEnumerable<EstadosViewModel> estados { get; set; }
}
change the model
public class ViewModel
{
public string ddl {get; set;}
public IEnumerable<SelectListItem> estadoItems { get; set; }
public UsuariosViewModel usuario { get; set; }
public IEnumerable<TiposUsuariosViewModel> tiposUsuarios { get; set; }
public IEnumerable<EstadosViewModel> estados { get; set; }
}
in your action add estado items to a view model
estadoItems= context.estados.Select(item => new SelectListItem
{
Value = item.Id_Estado.ToString(),
Text = item.Nombre_Estado,
}).ToList();
viewModel.estadoItems = estadoItems
and view
#Html.DropDownListFor(model=>model.ddl, #Model.estadoItems, new { #class = "form-select", aria_label="Default select example" });
but it is better to use select, since it is automatically selects item from list
<select class="form-control" asp-for="ddl" asp-items="#Model.estadoItems"> select </select>
I am assuming the you want to send the value of the selected option to your Controller method. Now since you have not shown your Controller method, I will give a basic example using AJAX and Jquery:
First give an id to your drop down list:
#Html.DropDownList("ddl", Model.estados.Select(item => new SelectListItem
{
Value = item.Id_Estado.ToString(),
Text = item.Nombre_Estado,
Selected = "select" == item.Id_Estado.ToString()
}), new { #class = "form-select", aria_label="Default select eaxmple", #id="myddl" }
)
You can have a button which will invoke the event or whatever event you are using, you can do that. I am using a button event here:
<input type="button" value="Process Input" class="btn btn-primary btn-lg btn-block" id="mySubmitbtn" />
Then you can use AJAX to send it to your Controller method and get a response back:
$(document).ready(function () {
$("#mySubmitbtn").click(function () {
var mySelectedValue= $('#myddl').find(":selected").text();
var json = {
mySelectedValue: mySelectedValue
};
var options = {};
options.url = "#Url.Action("ProcessInput", "Home")";
options.type = "POST";
options.data = {"json": JSON.stringify(json)};
options.contentType = "application/json";
options.dataType = "json";
options.success = function (msg) {
alert("Successfully processed");
};
options.error = function () {
alert("Error");
};
$.ajax(options);
})
});
And finally your Controller method will be:
using System.Web.Script.Serialization;
[HttpPost]
public JsonResult ProcessInput(string json)
{
var serializer = new JavaScriptSerializer();
dynamic jsondata = serializer.Deserialize(json, typeof(object));
//Get your variables here from AJAX call
var mySelectedValue = jsondata["mySelectedValue"];
//Do your stuff
}

Save jQuery JSON object into SQL table without creating View model class in MVC ASP.NET Core

I am working on reading JSON data from the URL and insert it into the SQL table. I have used this sample URL https://raw.githubusercontent.com/wedeploy-examples/supermarket-web-example/master/products.json and create a Model class file as below.
View Model Class
public class ApiJsonViewModel
{
public string Title { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public string Filename { get; set; }
public string Height { get; set; }
public string Width { get; set; }
public string Price { get; set; }
public string Rating { get; set; }
}
I have a form with one textbox control to display JSON key data from the third-party API URL and a drop-down list with values from the database.
The model class used to populate dropdownlist
public class K360DbCatgViewModel
{
public string Name { get; set; }
public string MetaTitle { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public string ShortDescription { get; set; }
public string Description { get; set; }
public string Specification { get; set; }
public decimal Price { get; set; }
public decimal? OldPrice { get; set; }
public decimal? SpecialPrice { get; set; }
public DateTimeOffset? SpecialPriceStart { get; set; }
public DateTimeOffset? SpecialPriceEnd { get; set; }
public int StockQuantity { get; set; }
public string Sku { get; set; }
public string Gtin { get; set; }
public string NormalizedName { get; set; }
public int DisplayOrder { get; set; }
public int ReviewsCount { get; set; }
public double? RatingAverage { get; set; }
}
Razor View Page
<table class="table" id="tb_properties" style="width:100%">
<tr>
#if (ViewBag.ApiProp != null)
{
#foreach (var itemApiProp in ViewBag.ApiProp)
{
<td>
<input type="text" value="#itemApiProp.Key" class="form-control" />
<select class="form-control">
<option value="">--Select-- </option>
#foreach (var itemK360Prop in ViewBag.K360Prop)
{
<option>#itemK360Prop.Key</option>
}
</select>
</td>
}
}
</tr>
<tr>
<td>
<button type="submit" class="btn btn-primary" style="margin-
right:50px">Catalog Mapping</button>
</td>
</tr>
</table>
Controller code to fetch values for the view controls
public IActionResult Index()
{
string strAPIUrl = "https://raw.githubusercontent.com/wedeploy-examples/supermarket-web-example/master/products.json";
string jsonUrlProducts;
using (WebClient client = new WebClient())
{
jsonUrlProducts = client.DownloadString(strAPIUrl);
}
CreateDynamicAPiProp(jsonUrlProducts);
CreateK360Prop();
return View();
}
[HttpGet]
public IActionResult CreateDynamicAPiProp(string ApiUrl)
{
Dictionary<string, object> dictResultsAdd = new Dictionary<string, object>();
var objResponseB = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(ApiUrl);
foreach (Dictionary<string, object> DictMainKV in objResponseB)
{
foreach (KeyValuePair<string, object> item in DictMainKV)
{
dictResultsAdd.Add(item.Key, item.Value);
}
break;
}
ViewBag.ApiProp = dictResultsAdd;
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult CreateK360Prop()
{
var ListK360Prop = new List<ApiMapDbViewModel>();
PropertyInfo[] propertyInfosK360 = typeof(K360DbCatgViewModel).GetProperties();
foreach (PropertyInfo propertyInfoK360 in propertyInfosK360)
{
ListK360Prop.Add(new ApiMapDbViewModel{Value = propertyInfoK360.Name.ToString(), Key = propertyInfoK360.Name.ToString()});
}
ViewBag.K360Prop = ListK360Prop;
return RedirectToAction("Index");
}
I have used the below jQuery and passed the JSON Model object to controller insert method on HTTP post to save selected records.
jQuery Code
#section scripts{
<script>
$(function() {
$("button[type='submit']").click(function() {
event.preventDefault();
var properties = [];
$("#tb_properties tr:first").find("td").each(function(index, item) {
var propertyname = $(item).find("input[type='text']").val();
var selctedvalue = $(item).find("select").val();
properties.push('"' + propertyname + '":"' + selctedvalue + '"');
});
var jsonstr = '{' + properties.join(",") + '}';
var jsobject = JSON.parse(jsonstr);
$.ajax({
type: "Post",
url: "/KEMap/Insert",
data: {
jsonModel: jsobject
},
success: function(response) {
toastr.info(response.status + "<br>" + "<br>" + response.message);
$("#tb_properties select").val("");
$("#partial_div").load(window.location.href + " #partial_div");
},
error: function(xhr, textStatus, errorThrown) {
console.log('in error');
}
});
});
});
</script>
Insert Method
[HttpPost]
public IActionResult Insert(ApiJsonViewModel jsonModel)
{
Type type = jsonModel.GetType();
PropertyInfo[] props = type.GetProperties();
List<K360mapMaster> K360mapListObj = new List<K360mapMaster>();
K360mapListObj = props.Where(c => !string.IsNullOrEmpty(c.GetValue(jsonModel, null)?.ToString())).Select(c => new K360mapMaster()
{ClientCatalog = c.Name, K360catalog = c.GetValue(jsonModel, null)?.ToString()}).ToList();
if (K360mapListObj.Count > 0)
{
_context.K360mapMasters.AddRange(K360mapListObj);
_context.SaveChanges();
return Json(new { Status = "Sucess", Message = "Mapped" });
}
return Json(new { Status = "Fail", Message = "Not done" });
}
SQL Table
CREATE TABLE [dbo].[K360Map_Master](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ClientCatalog] [nvarchar](450) NOT NULL,
[K360Catalog] [nvarchar](450) NOT NULL,
[MapFlag] [bit] NOT NULL,
CONSTRAINT [PK_K360Map_Master] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Everything working fine. I was able to insert selected textbox and dropdown list values into the SQL table.
The problem is JSON data I am using from the third-party API URL differ dynamically. For example for the below 2 sample URLs, I need to create again 2 model class file.https://gist.githubusercontent.com/dpetersen/1237910/raw/6ceb2161f756d4b4d5c22754d1ed8d869249f186/product_grid.js
https://raw.githubusercontent.com/mdn/fetch-examples/master/fetch-json/products.json.
I get the feedback as hardcoded coding with static data. I was not permitted to create a separate model class for each URL.
I also don't know how to proceed without creating a model class file. I need to pass selected control values to the controller insert method without a JSON model object.
I hope anybody can help me here.
Here is a working demo you could follow:
Model:
public class ApiMapDbViewModel
{
public string Value { get; set; }
public string Key { get; set; }
}
public class K360mapMaster
{
public string ClientCatalog { get; set; }
public string K360catalog { get; set; }
}
public class K360DbCatgViewModel
{
public string AA { get; set; }
public string BB { get; set; }
public string CC { get; set; }
public string DD { get; set; }
}
View:
<table class="table" id="tb_properties" style="width:100%">
<tr>
#if (ViewBag.ApiProp != null)
{
#foreach (var itemApiProp in ViewBag.ApiProp)
{
<td>
<input type="text" value="#itemApiProp.Key" class="form-control" />
<select class="form-control">
<option value="">--Select-- </option>
#foreach (var itemK360Prop in ViewBag.K360Prop)
{
<option>#itemK360Prop.Key</option>
}
</select>
</td>
}
}
</tr>
<tr>
<td>
<button type="submit" class="btn btn-primary" style="margin-
right:50px">
Catalog Mapping
</button>
</td>
</tr>
</table>
JS(Add data: jsonstr,contentType:"application/json"):
#section scripts{
<script>
$(function () {
$("button[type='submit']").click(function () {
event.preventDefault();
var properties = [];
$("#tb_properties tr:first").find("td").each(function (index, item) {
var propertyname = $(item).find("input[type='text']").val();
var selctedvalue = $(item).find("select").val();
properties.push('"' + propertyname + '":"' + selctedvalue + '"');
});
var jsonstr = '{' + properties.join(",") + '}';
//var jsobject = JSON.parse(jsonstr);
$.ajax({
type: "Post",
url: "/KEMap/Insert",
//data: jsobject,
data: jsonstr,
contentType:"application/json",
success: function (response) {
toastr.info(response.status + "<br>" + "<br>" + response.message);
$("#tb_properties select").val("");
$("#partial_div").load(window.location.href + " #partial_div");
},
error: function (xhr, textStatus, errorThrown) {
console.log('in error');
}
});
});
});
</script>
}
Controller:
[HttpPost]
public IActionResult Insert([FromBody]JObject jsonModel)
{
Type type = jsonModel.GetType();
PropertyInfo[] props = type.GetProperties();
List<K360mapMaster> K360mapListObj = new List<K360mapMaster>();
foreach (JProperty prop in jsonModel.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
K360mapListObj.Add(new K360mapMaster() { ClientCatalog = key, K360catalog = value });
}
//do your stuff...
return Json(new { Status = "Fail", Message = "Not done" });
}
Note 1:
Because the mapped property in ApiJsonViewModel capitalize the first letter,so the data you get here ClientCatalog = c.Name, is capital.From my provided code,The code here string key = prop.Name.ToString(); get the json(https://raw.githubusercontent.com/wedeploy-examples/supermarket-web-example/master/products.json) key name which is lower case.If you want to keep the first letter capital,you could change the following line:
string key = prop.Name.ToString().First().ToString().ToUpper() + prop.Name.ToString().Substring(1);
Note 2:
If your project is asp.net core 3.x or asp.net 5,be sure your project has Newtonsoft support,otherwise,you cannot pass data to backend successfully.More details you could refer to:
.NET 5.0 MVC return Json is throwing a JSON Parser error
Result:

Get infos of submit button after a foreach

I try to put some info into a new modal :
when I click on a button I want to open a new modal with good info of clicked pokemon.
I made controller :
public IActionResult Index()
{
#region ListeDesPokemons
var pokemonList = new List<PokemonModel>();
var Id = 1;
var Img = 1;
pokemonList.Add(new PokemonModel() { Id = Id++, Name = "Bulbizarre", UsName = "Bulbasaur(us)", JpName = "フシギダネ(jp)", Type1 = "Plante", Type2 = "Poison", Rate = 45, Image = "https://www.pokemontrash.com/pokedex/images/sugimori/00" + Img++ + ".png" });
pokemonList.Add(new PokemonModel() { Id = Id++, Name = "Herbizarre", UsName = "Ivysaur(us)", JpName = "フシギソウ(jp)", Type1 = "Plante", Type2 = "Poison", Rate = 45, Image = "https://www.pokemontrash.com/pokedex/images/sugimori/00" + Img++ + ".png" });
var model = new PokemonViewModel();
model.Pokemons = pokemonList;
return View(model);
I made a Viewmodel :
public List<PokemonModel> Pokemons { get; set; }
public List<PokeBallModel> PokeBalls { get; set; }
public List<PokemonStatutModel> PokemonStatuts { get; set; }
}
I made a Model :
public int Id { get; set; }
public string Name { get; set; }
public string UsName { get; set; }
public string JpName { get; set; }
public string Type1 { get; set; }
public string Type2 { get; set; }
public int Rate { get; set; }
public string Image { get; set; }
I made a view :
#foreach (var pokemon in Model.Pokemons){ #pokemon.Id,#pokemon.Name #pokemon.Image}
here a picture of the first modal on the back of the screen with all pokemon list (foreach)
and the second modal on the front of the screen with no info.
please help.
I think you don't need to use form and should use ajax, when user click the picture of pokemon, you call an ajax with the Id of this pokemon, and in the controller you search all infos of pokemon with Id, and success in ajax just add infomations to the modal dialog. Something like:
<img class="align-self-center" id="tailleImg" src="#pokemon.Image" alt="#pokemon.Name" onclick="getInfo(#pokemon.Id)" />
function getInfo(val)
{
$.ajax({
url: '/yourController/yourActionToGetInfo',
type: "POST",
dataType: "json",
data: { "pokeId": val},
success: function (data) {
$("#pokeId").val(data.Id);
}
})
}
ok
In my view, in my foreach I add :
#foreach (var pokemon in Model.Pokemons)
{
<div class="col">
<button data-pokemon-id="#pokemon.Id" type="submit" class="btn" onclick="getInfo();">
<div class="card text-center rounded-lg">
<div id="tailleCard" class="card-body">
<h5 id="cardTitle" class="card-title">n°#pokemon.Id <br /> #pokemon.Name</h5>
<img id="tailleImg" src="#pokemon.Image" alt="#pokemon.Name" />
</div>
</div>
</button>
</div>
}
# the end of the view I add : But the Request does not exist in the current context nedd help please.
<script>
function getInfo() {
$.post('#(Url.Action("PokemonDetails", "Pokedex", null, Request.Url.Scheme))?pokemonId=' +
$(this).data("pokemon-id"))
.done(function (response) {
$("#divContent").empty();
$("#divContent").html(response);
$("#divContent").html(response);
$('#pokemonDetailsModal').modal('show');
});
</script>
In the controller I add a :
public IActionResult PokemonDetails(int pokemonId)
{
var model = new PokemonDetails();
return PartialView("_PokemonDetails", model);
}
I also made a Partial view _ and a PokemonDetail Model
public class PokemonDetails
{
public string Name { get; set; }
}
What I try to do it's to take the data of the selected pokemon (with the button in the foreach) and put the data of the selected pokemon in New Modal.
OK i almost there ! cool
in the view
...
#foreach (var pokemon in Model.Pokemons)
{
<button type="submit" class="btn" onclick="getInfo(#pokemon.Id)">
<div class="card text-center rounded-lg">
<div id="tailleCard" class="card-body">
<h5 id="cardTitle" class="card-title">n°#pokemon.Id <br /> #pokemon.Name</h5>
<img id="tailleImg" src="#pokemon.Image" alt="#pokemon.Name" />
</div>
</div>
</button>
}
...
at the end of the view page :
<script>
function getInfo(val) {
event.preventDefault;
//debug log
console.log('Star getInfo()');
$.ajax({
url: '/Pokedex/PokemonDetails/',
type: 'POST',
dataType: "html",
data: { "PokemonId": val },
//data: json,
//contentType: 'application/json; charset=utf-8',
success: function (response) {
//debug log
console.log(val);
$("#pokemonDetails").html(response);
$('#pokemonDetails').modal('show');
}
})
}
</script>
in the controller :
[HttpPost]
public IActionResult PokemonDetails(int PokemonId)
{
int SelectedPokemonId = PokemonId;
TempData["SelectedPokemonId"] = SelectedPokemonId;
ToDoo : Get Info of the pokemon from the Id
return PartialView("_PokemonDetails");
}
How I can get the info of the selected pokemon from the selected Id???
https://i.stack.imgur.com/atajO.png
[HttpPost]
public IActionResult PokemonDetails(int PokemonId)
{
int SelectedPokemonId = PokemonId;
TempData["SelectedPokemonId"] = SelectedPokemonId;
ToDoo : Get Info of the pokemon from the Id
return PartialView("_PokemonDetails");
}
Do you have database ? if you have just call:
return (db.PokemonModel.FirstOrDefault(n=>n.Id == PokemonId));
and if you don't have use the list you use in action Index:
return pokemonList.FirstOrDefault(n=>n.Id == PokemonId));
and in sucess ajax:
success: function (response) {
$("#cardTitle").html("n°" + response.Id +"<br />" + response.Name");
$("#tailleImg").attr('src', response.Image);
$("#tailleImg").attr('name', response.Name);
$('#pokemonDetails').modal('show');
}

ASP.NET MVC Core Cascading DropDownList

I'm having trouble finding a tutorial / video that shows how to implement Cascading DropDownList from a Database using EntityFramework. I'm using ASP.NET MVC Core, EntityFramework Core with C#.
As of now, I'm able to retrieve the data from my database to my 3 DropDownList fine.
What I would like to be able to accomplish is to have the user select a State first which would then display all Cities related to that State. Then after user has selected a City it would display the Zip Code(s) related to the City.
Any help would be greatly appreciated.
Models
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int StateId { get; set; }
public int CityId { get; set; }
public int ZipId { get; set; }
public State State { get; set; }
public City City { get; set; }
public Zip Zip { get; set; }
}
public class State
{
public int StateId { get; set; }
public string Abbr { get; set; }
public List<Customer> Customers { get; set; }
}
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public int StateId { get; set; }
public State State { get; set; }
public List<Customer> Customers { get; set; }
}
public class Zip
{
public int ZipId { get; set; }
public string PostalCode { get; set; }
public int CityId { get; set; }
public City City { get; set; }
public List<Customer> Customers { get; set; }
}
ViewModels
public class CustomerFormVM
{
public int CustomerId { get; set; }
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
[Required(ErrorMessage = "Select State")]
[Display(Name = "State")]
public int StateId { get; set; }
//public IEnumerable<State> States { get; set; }
public IEnumerable<SelectListItem> States { get; set; }
[Required(ErrorMessage = "Select City")]
[Display(Name = "City")]
public int CityId { get; set; }
//public IEnumerable<City> Citys { get; set; }
public IEnumerable<SelectListItem> Citys { get; set; }
[Required(ErrorMessage = "Select Zip")]
[Display(Name = "Zip")]
public int ZipId { get; set; }
//public IEnumerable<Zip> Zips { get; set; }
public IEnumerable<SelectListItem> Zips { get; set; }
}
CustomerController
public class CustomerController : Controller
{
private MultiDbContext db;
public CustomerController(MultiDbContext context)
{
db = context;
}
// GET: /<controller>/
public IActionResult Index()
{
return View(db.Customers.ToList());
}
public IActionResult getCititesFromDatabaseByStateId(int id)
{
return View(db.Citys.Where(c => c.StateId == id).ToList());
}
public IActionResult getCities(int id)
{
var cities = new List<City>();
cities = getCititesFromDatabaseByStateId(id); //call repository
return Json(cities);
}
public ActionResult Create()
{
var states = db.States.ToList();
var citys = db.Citys.ToList();
var zips = db.Zips.ToList();
var viewModel = new CustomerFormVM
{
States = states,
Citys = citys,
Zips = zips
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CustomerFormVM vm)
{
if (ModelState.IsValid)
{
var customer = new Customer();
{
customer.FirstName = vm.FirstName;
customer.LastName = vm.LastName;
customer.StateId = vm.StateId;
customer.CityId = vm.CityId;
customer.ZipId = vm.ZipId;
}
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
vm.States = db.States.ToList();
vm.Citys = db.Citys.ToList();
vm.Zips = db.Zips.ToList();
return View(vm);
}
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var customervm = new CustomerFormVM();
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == id);
if (customer == null)
{
return NotFound();
}
customervm.CustomerId = customer.CustomerId;
customervm.FirstName = customer.FirstName;
customervm.LastName = customer.LastName;
// Retrieve list of States
var states = db.States.ToList();
customervm.States = states;
// Retrieve list of Citys
var citys = db.Citys.ToList();
customervm.Citys = citys;
// Retrieve list of Citys
var zips = db.Zips.ToList();
customervm.Zips = zips;
// Set the selected state
customervm.StateId = customer.StateId;
// Set the selected city
customervm.CityId = customer.CityId;
// Set the selected zip
customervm.ZipId = customer.ZipId;
}
return View(customervm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomerFormVM vmEdit)
{
if (ModelState.IsValid)
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == vmEdit.CustomerId);
if (customer == null)
{
return NotFound();
}
customer.FirstName = vmEdit.FirstName;
customer.LastName = vmEdit.LastName;
customer.StateId = vmEdit.StateId;
customer.CityId = vmEdit.CityId;
customer.ZipId = vmEdit.ZipId;
db.Entry(customer).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vmEdit);
}
}
Create View
<div class="form-group">
#Html.LabelFor(c => c.FirstName)
#Html.TextBoxFor(c => c.FirstName, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(c => c.LastName)
#Html.TextBoxFor(c => c.LastName, new { #class = "form-control" })
</div>
<div class="form-group">
#*#Html.LabelFor(s => s.StateId)
#Html.DropDownListFor(s => s.StateId, new SelectList(Model.States, "StateId", "Abbr"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(s => s.StateId)*#
<label asp-for="StateId "></label>
<select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
<span asp-validation-for="StateId " class="text-danger"></span>
</div>
<div class="form-group">
#*#Html.LabelFor(ct => ct.CityId)
#Html.DropDownListFor(ct => ct.CityId, new SelectList(Model.Citys, "CityId", "Name"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(ct => ct.CityId)*#
<label asp-for="CityId"></label>
<select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
<span asp-validation-for="CityId" class="text-danger"></span>
</div>
<div class="form-group">
#Html.LabelFor(z => z.ZipId)
#Html.DropDownListFor(z => z.ZipId, new SelectList(Model.Zips, "ZipId", "PostalCode"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(z => z.ZipId)
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
}
#section scripts {
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
<script src="~/lib/js/example.js"></script>,
}
I had a similar situation but in my example I have a Root folder and depending on which root folder I am using the next drop down list would display the corresponding sub-folders.
Not sure if there is a purly asp.net solution but, I used Jquery/Ajax for this.
Your code should look something like this:
html list:
<label asp-for="StateId "></label>
<select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
<span asp-validation-for="StateId " class="text-danger"></span>
<label asp-for="CityId"></label>
<select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
<span asp-validation-for="CityId" class="text-danger"></span>
Jquery code, you write this in .js file and then add it to a specific view with this statement<script src="~/js/example.js"></script>, Don't forget you need to add a jquery library to your project before any other javascript, and your example.js will contain:
$(document).ready(function () {
$("#state-target").on("change", function () {
$list = $("#city-target");
$.ajax({
url: "/getCities",
type: "GET",
data: { id: $("#state-target").val() }, //id of the state which is used to extract cities
traditional: true,
success: function (result) {
$list.empty();
$.each(result, function (i, item) {
$list.append('<option value="' + item["CityId"] + '"> ' + item["Name"] + ' </option>');
});
},
error: function () {
alert("Something went wrong call the police");
}
});
});
});
The Ajax request will call this action in the Controller which will retrieve a list of cities from the database (using something like return dbContext.CityTable.Where(c => c.StateId == id).ToList() inside a getCititesFromDatabaseByStateId(id) method) and then return the Json object, the success function will create a list of options and apply it:
public IActionResult getCities(int id)
{
var cities = new List<City>();
cities = getCititesFromDatabaseByStateId(id); //call repository
return Json(citites);
}
In your ViewModel consider changing IEnumerable<State/City/Zip> (IEnumerable<T>) to IEnumerable<SelectListItem>. I can say as well your Model's are messy (but if you can get data the from the database focus on getting the list working 1st), consider improving them later.
Fix for 2 errors mentioned in the comments:
public List<City> getCititesFromDatabaseByStateId(int id)
{
return db.Citys.Where(c => c.StateId == id).ToList();
}
public ActionResult Create()
{
var states = new SelectList(db.States.ToList(), "StateId", "Abbr");
var citys = new SelectList(db.Citys.ToList(), "CityId", "Name");
var zips = new SelectList(db.Zips.ToList(), "ZipId", "Code");
var viewModel = new CustomerFormVM
{
States = states,
Citys = citys,
Zips = zips
};
return View(viewModel);
}

ASP MVC knockout json nested foreach data binding

Sorry if this particular question has been asked already, I have been through countless similar questions but none that seem to relate with my problem.
I am fairly new to MVC Web API's and working with JavaScript/knockout.
View model script - QuestionItems.js
var SectionModel = function (data) {
var self = this;
self.SectionName = ko.observable(data.SectionName);
self.SectionNumber = ko.observable(data.SectionNumber);
self.Questions = ko.observableArray();
};
var questionnaireViewModel = function () {
self.sections = ko.observableArray();
self.error = ko.observable();
var itemsUri = '/api/QuestionnaireItems/';
var sectionsUri = '/api/QuestionnaireSections/';
function ajaxHelper(uri, method, data) {
self.error('');
return $.ajax({
type: method,
url: uri,
dataType: 'json',
contentType: 'application/json',
data: data ? JSON.stringify(data) : null
}).fail(function (jqXHR, textStatus, errorThrown) {
self.error(errorThrown);
});
}
function getAllQuestions() {
ajaxHelper(itemsUri, 'GET').done(function (data) {
for (var s = 0; s < self.sections.length; s++) {
// find all the questions for this section
var sectionQuestions = data.filter(function (item) {
return item.SectionName == self.sections[s].SectionName;
});
// add the questions to each section
self.sections.Questions(sectionQuestions);
}
});
}
function getAllSections() {
ajaxHelper(sectionsUri, 'GET').done(function (data) {
// map each data item to a SectionModel and store that in your main viewModel
var sectionsTemp = data.map(function (item) {
return new SectionModel(item);
});
self.sections(sectionsTemp);
});
}
getAllSections();
getAllQuestions();
};
ko.applyBindings(questionnaireViewModel);
And my view - Index.cshtml
#section scripts {
#Scripts.Render("~/bundles/QuestionItems")
}
<div class="col-lg-12">
<h1 class="page-header">SQA</h1>
<ul class="list-unstyled" data-bind="foreach: sections">
<li>
<div class="panel panel-default">
<div class="panel-heading">
<div><strong data-bind="text: SectionNumber"></strong>. <strong data-bind="text: SectionName"></strong></div>
</div>
<div class="panel-body">
<ul data-bind="foreach: Questions">
<li>
<span data-bind="text: QuestionNumber"></span>. <span data-bind="text: QuestionName"></span>
</li>
</ul>
</div>
</div>
</li>
</ul>
</div>
In the database I have a QuestionnaireItems table which stores all question details and a foreign key to the sections table to retrieve each questions related section :
QuestionnaireItems
And a QuestionnaireSections table which holds each sections name and order number :
QuestionnaireSections
I have managed to get the sections displaying, however the questions are not. If I pull through just the questions without matching them to their correct sections they display. I'm assuming this is a problem with the way the condition is set up but I am at a complete loss as to how I can fix this.
I have also tested the controllers in Postman and all the correct data is pulling through.
QuestionItemList.cs (DTO):
public class QuestionItemList
{
public int Id { get; set; }
public int SectionNumber { get; set; }
public string SectionName { get; set; }
public int QuestionNumber { get; set; }
public string QuestionName { get; set; }
}
SectionItemList.cs (DTO)
public class SectionItemList
{
public int Id { get; set; }
public int SectionNumber { get; set; }
public string SectionName { get; set; }
}
QuestionnaireItemsController:
public class QuestionnaireItemsController : ApiController
{
private SQAContext db = new SQAContext();
// GET: api/QuestionnaireItems
[HttpGet]
public IQueryable<QuestionItemList> GetItems()
{
var items = from a in db.QuestionnaireItems
select new QuestionItemList()
{
Id = a.PID,
SectionNumber = a.QuestionnaireSection.Number,
SectionName = a.QuestionnaireSection.Name,
QuestionNumber = a.Number,
QuestionName = a.Name
};
return items;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool QuestionnaireItemExists(int id)
{
return db.QuestionnaireItems.Count(e => e.PID == id) > 0;
}
}
QuestionnaireSectionsController:
public class QuestionnaireSectionsController : ApiController
{
private SQAContext db = new SQAContext();
// GET: api/QuestionnaireSections
[HttpGet]
public IQueryable<SectionItemList> GetSectionItems()
{
var items = from a in db.QuestionnaireSections
select new SectionItemList()
{
Id = a.PID,
SectionNumber = a.Number,
SectionName = a.Name
};
return items;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool QuestionnaireSectionExists(int id)
{
return db.QuestionnaireSections.Count(e => e.PID == id) > 0;
}
}

Categories