TokenProvider.RequestAccessToken() does not bring token AuthenticationState has idp_access_token, session cookie "credentialType": "IdToken" exists - access-token

I have a Blazor web assembly app authenticated by AD. The call to await TokenProvider.RequestAccessToken() does not bring the access token even though it is in the session storage and can be retrieved from the AuthenticationState as shown in the code below. Why is the tokenResult.Status shows "RequiresRedirect" and has no token value?
#page "/"
#using System.Security.Claims;
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<PageTitle>Index</PageTitle>
<AuthorizeView>
<Authorized>
<table width="90%">
<tr>
<th style="width: 200px;">Claim Type</th>
<th>Claim Value</th>
</tr>
#foreach (var claim in User.Claims)
{
<tr>
<td>#claim.Type.ToString()</td>
<td class="tdValue">#claim.Value</td>
</tr>
}
</table>
<p>#idp_access_token?.Value</p>
</Authorized>
</AuthorizeView>
#code {
[Inject] IAccessTokenProvider TokenProvider { get; set; }
[CascadingParameter]
private Task<AuthenticationState>? AuthenticationStateTask { get; set; }
private ClaimsPrincipal User;
private AccessToken idp_access_token;
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateTask;
if (authState.User.Identity is { IsAuthenticated: true })
{
User = authState.User;
}
var tokenResult = await TokenProvider.RequestAccessToken();
//at this point tokenResult.Status has value of "RequiresRedirect" and the token is null
if (tokenResult.TryGetToken(out var token))
{
idp_access_token = token;
}
}
}
[UPDATE]
I managed to resolve the problem by adding in the program.cs class options.ProviderOptions.LoginMode = "Redirect" like this:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("....");
options.ProviderOptions.LoginMode = "Redirect";
});

Related

React/Typescript how do to load images?

I have a question about loading images into the reaction project. I use typescript and pass objects to the Table1 component as props. Some properties include the directories to load the images. My directories are in the public folder. The images are not loading and I don't know why.
This is the App:
//MY APP function
import React from 'react';
import './App.css';
import { Table1 } from './components/Table1';
import {Data} from './models/Data';
export const App = () => {
const data:Data[] = [
new Data("Gustav23", **"../public/images/typicalUser.png"**, 2), //I M A G E P A T H
new Data("Lex12", **../public/images/choice.png", 0),
new Data("Max&1", **"../public/images/customer.png", 30)
]
return (
<div className="App">
<Table1 data={data}/>
</div>
);
}
This is t´my Component:
import React from 'react';
import { Data } from '../models/Data';
export const Table1 : React.FC<{data: Data[]}>= (props)=>{
return (
<div>
<table>
<thead>
<tr>
<th scope="row">Username</th>
<th scope="row">Picture</th>
<th scope="row">Posts</th>
</tr>
</thead>
<tbody>
{props.data.map(dat =>(
<tr>
<td>{dat.nick}</td>
<td><img
src={dat.img}
width="150"
height= "210"
alt = "UserIcon"
/>
</td>
<td>{dat.count}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
And last my Model:
export class Data{
constructor(private _nick:string, private _img:string, private _count:number, ){
}
getNick():string{
return this._nick;
}
getImg():string{
return this._img;
}
setNick(value:string): void{
this._nick = value;
}
setImg(value:string):void{
this._img= value;
}
getCount():number{
return this._count;
}
setCount(value:number):void{
this._count = value;
}
get nick(){
return this._nick;
}
get img(){
return this._img;
}
get count(){
return this._count;
}
//------------------
set nick(value:string){
this._nick = value;
}
set img(value:string){
this._img = value;
}
set count(value:number){
this._count= value;
}
}
i tried to use require()-Method, my images were in src/images/ folder.
I want my pictures to be displayed.

How to persist model data when form post

When form is showing initially then I show question and their answer. I show multiple radio button for answer. So user can select only one answer. The problem occurs when I submit my form and action method calls. I saw when form post then answer model is getting null. So guide me how to write code as a result answer model should not be null when form will post.
Here is full code. Please go through the code and if requires changes as a result answer model will not be null when form will be posted to action method.
my ViewModels code
namespace ViewModels
{
public class Question
{
public int ID { set; get; }
public string QuestionText { set; get; }
public List<Answer> Answers { set; get; }
[Required]
public string SelectedAnswer { set; get; }
public Question()
{
Answers = new List<Answer>();
}
}
public class Answer
{
public int ID { set; get; }
public string AnswerText { set; get; }
}
public class Evaluation
{
public List<Question> Questions { set; get; }
public Evaluation()
{
Questions = new List<Question>();
}
}
}
controller code
public ActionResult Index()
{
var evalVM = new Evaluation();
//the below is hardcoded for DEMO. you may get the data from some
//other place and set the questions and answers
var q1 = new Question { ID = 1, QuestionText = "What is your favourite language" };
q1.Answers.Add(new Answer { ID = 12, AnswerText = "PHP" });
q1.Answers.Add(new Answer { ID = 13, AnswerText = "ASP.NET" });
q1.Answers.Add(new Answer { ID = 14, AnswerText = "Java" });
evalVM.Questions.Add(q1);
var q2 = new Question { ID = 2, QuestionText = "What is your favourite DB" };
q2.Answers.Add(new Answer { ID = 16, AnswerText = "SQL Server" });
q2.Answers.Add(new Answer { ID = 17, AnswerText = "MySQL" });
q2.Answers.Add(new Answer { ID = 18, AnswerText = "Oracle" });
evalVM.Questions.Add(q2);
return View(evalVM);
}
[HttpPost]
public ActionResult Index(Evaluation model)
{
if (ModelState.IsValid)
{
foreach (var q in model.Questions)
{
if(q.Answers==null)
{
// Answers is null
}
var qId = q.ID;
var selectedAnswer = q.SelectedAnswer;
// Save the data
}
return RedirectToAction("ThankYou"); //PRG Pattern
}
//reload questions
return View(model);
}
index.cshtml code
#model ViewModels.Evaluation
<h2>Quiz 24</h2>
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x.Questions)
<input type="submit" />
}
and view code which is stored in EditorTemplates folder
#model ViewModels.Question
<div>
#Html.HiddenFor(x=>x.ID)
<h3> #Model.QuestionText </h3>
#foreach (var a in Model.Answers)
{
<p>
#Html.RadioButtonFor(b=>b.SelectedAnswer,a.ID) #a.AnswerText
</p>
}
</div>
the problem is here
[HttpPost]
public ActionResult Index(Evaluation model)
{
if (ModelState.IsValid)
{
foreach (var q in model.Questions)
{
if(q.Answers==null)
{
// Answers is null
}
var qId = q.ID;
var selectedAnswer = q.SelectedAnswer;
// Save the data
}
return RedirectToAction("ThankYou"); //PRG Pattern
}
//reload questions
return View(model);
}
q.Answers==null is getting null when i post the form. i like to know the trick that how to write code in such a way when form will be post to action then Answers should be null.
many guy told me that i need to rebuild the model manually because Answers will be always null. is there any no mechanism in MVC to persist all the data and properly De-serialize it to model when form will be posted.
AnswerText Normally I would recommend using another nested Editor Template for the Answer model, but in this case it will cause issues to properly construct the radio-button group and aftwerwards retrieve the value of SelectedAnswer. As a simple solution I would adapt the Editor Template for Question model in the following way:
<div>
#Html.HiddenFor(x=>x.ID)
<h3> #Model.QuestionText </h3>
<div>
#for (int i = 0; i < Model.Answers.Count; i++)
{
<p>
#Html.RadioButtonFor(model => model.SelectedAnswer, Model.Answers[i].AnswerText #Model.Answers[i].AnswerText
#Html.HiddenFor(model => model.Answers[i].ID)
#Html.HiddenFor(model => model.Answers[i].AnswerText)
</p>
}
</div>
</div>
This will create the following HTML (skipped not-relevant attributes):
<input type="radio" name="Questions[0].SelectedAnswer" ...>
<input type="hidden" name="Questions[0].Answers[0].ID" ...>
<input type="hidden" name="Questions[0].Answers[0].AnswerText" ...>
Output will allow MVC to properly reconstruct the Answer collection.
Update
In your original code you were looking for the Answer.AnswerText, so I've updated the code to include it instead of the Answer.ID.
Hope this helps.

Recommended way to implement array of Ajax cascading dropdown lists in MVC4/Razor?

I am writing an ASP.NET MVC4 / Razor / C# based application that needs to render a grid of records. Each row has several columns, and there may be 100 or so rows.
Each row has a checkbox field, text field and then three cascading dropdown lists. The first dropdown is prepopulated on page load. The second needs to be populated using Ajax on change of the first dropdown list. The third from a change on the second. Each row is separate and does not influence each other.
What is the recommended way to implement something like this? The various solutions for cascading dropdown lists are only for single cascading lists - they do not work (for me) when placed inside a foreach loop.
The skeleton of what I have is shown below:
#model IList<MyModel>
#using (Html.BeginForm("MyAction", "Home")) {
<table><tr><th>Use?</th><th>Name</th><th>List A</th><th>List B</th><th>List C</th></tr>
#Html.EditorForModel()
</table>
}
The model looks something like this:
public class MyModel
{
public bool Use { get; set; }
public string Name { get; set; }
public int? ListAId { get; set; }
public int? ListBId { get; set; }
public int? ListCId { get; set; }
public IList<ListAList> ListA { get; set; }
}
The shared EditorTemplates file MyModel.cshtml follows this structure:
#model MyNamespace.MyModel
<tr>
<td>#Html.CheckBoxFor(model => model.Use)</td>
<td>#Html.DisplayFor(model => model.Name)</td>
<td>#Html.DropDownListFor(model => model.ListAId, new SelectList(Model.ListA, "Id", "Name", Model.ListAId), "")</td>
<td>??</td>
<td>??</td>
</tr>
The ?? indicates I am unsure what to put in here.
How do I proceed to render the ListB select box using Ajax on change of the ListA select box, then on change of ListB render the ListC select box?
check this:
on select change event - Html.DropDownListFor
http://addinit.com/node/19
or
http://www.codeproject.com/Articles/258172/Simple-Implementation-of-MVC-Cascading-Ajax-Drop-D
Update1: Suppose that there is Name ROWID, (and list all the same data source).
Update2: the example available on github
Based on these responses:
How to populate a #html.dropdownlist mvc helper using JSon
Populate a <select></select> box with another
Model:
using System.Collections.Generic;
namespace MyNamespace
{
public class MyModel
{
public MyModel() { ListA = new List<ListAList>(); }
public bool Use { get; set; }
public string Name { get; set; }
public int? ListAId { get; set; }
public int? ListBId { get; set; }
public int? ListCId { get; set; }
public IList<ListAList> ListA { get; set; }
}
public class ListAList
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Main action in the Home Controller:
public ViewResult MyAction()
{
var model = new List<MyModel>();
for (int i = 0; i < 10; i++)
{
var item = new MyModel()
{
Name = string.Format("Name{0}", i),
Use = (i % 2 == 0),
ListAId = null,
ListBId = null,
ListCId = null
};
for (int j = 0; j < 10; j++)
{
item.ListA.Add( new ListAList()
{
Id=j,
Name = string.Format("Name {0}-{1}",i,j)
});
}
model.Add(item);
}
return View(model);
}
Data source provider in the Home controller:
public JsonResult PopulateOption(int? listid, string name)
{
//todo: preparing the data source filter
var sites = new[]
{
new { id = "1", name = "Name 1" },
new { id = "2", name = "Name 2" },
new { id = "3", name = "Name 3" },
};
return Json(sites, JsonRequestBehavior.AllowGet);
}
EditorTemplate:
#model MyNamespace.MyModel
<tr>
<td>#Html.CheckBoxFor(model => model.Use)</td>
<td>#Html.DisplayFor(model => model.Name)</td>
<td>#Html.DropDownListFor(model => model.ListAId, new SelectList(Model.ListA, "Id", "Name", Model.ListAId), "", new { #id = string.Format("ListA{0}", Model.Name), #class="ajaxlistA" })</td>
<td><select class="ajaxlistB" id="ListB#(Model.Name)"></select></td>
<td><select class="ajaxlistC" id="ListC#(Model.Name)"></select></td>
</tr>
And the main view with Ajax functions:
#using MyNamespace
#model IList<MyModel>
#using (Html.BeginForm("MyAction", "Home")) {
<table><tr><th>Use?</th><th>Name</th><th>List A</th><th>List B</th><th>List C</th></tr>
#Html.EditorForModel()
</table>
}
<script src="#Url.Content("~/Scripts/jquery-1.7.1.js")" type="text/javascript"></script>
<script>
$(document).ready( $(function () {
$('.ajaxlistA').change(function () {
// when the value of the first select changes trigger an ajax request
list = $(this);
var listvalue = list.val();
var listname = list.attr('id');
$.getJSON('#Url.Action("PopulateOption", "Home")', { listid: listvalue, name: listname }, function (result) {
// assuming the server returned json update the contents of the
// second selectbox
var listB = $('#' + listname).parent().parent().find('.ajaxlistB');
listB.empty();
$.each(result, function (index, item) {
listB.append(
$('<option/>', {
value: item.id,
text: item.name
})
);
});
});
});
$('.ajaxlistB').change(function () {
// when the value of the first select changes trigger an ajax request
list = $(this);
var listvalue = list.val();
var listname = list.attr('id');
$.getJSON('#Url.Action("PopulateOption", "Home")', { listid: listvalue, name: listname }, function (result) {
// assuming the server returned json update the contents of the
// second selectbox
var listB = $('#' + listname).parent().parent().find('.ajaxlistC');
listB.empty();
$.each(result, function (index, item) {
listB.append(
$('<option/>', {
value: item.id,
text: item.name
})
);
});
});
});
}));
</script>
And the result:

Simple Ajax in asp.net MVC 3, update the model and rerender part

I come from a more WPF application background and I'm used to bindings and such. Jumping into websites then can come with it's problems as they work so much more differently.
I'm trying to do a simple Ajax action but not sure where to begin. Basically I want to make a dropdownlist that changes one property on the model and re-renders that part of the page. Perhaps this is too much of a WPF way to do this so my Model could be twisted for the thing it is supposed to do.
Here is what I got already:
public class TheViewModel
{
private IEnumerable<TheData> _data;
public TheViewModel(IEnumerable<TheData> data)
{
_data = data;
Year = 2012;
}
public int Year { get; set; }
public ICollection<TheData> Data
{
get
{
return _data.Where(d => d.Year == this.Year).ToList();
}
}
public IEnumerable<SelectListItem> YearList
{
// lists the available years
}
}
public class TheData
{
public int Year { get; set; }
//Some more info I want to represent in Table
}
And the razor:
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "thetable" }))
{
#Html.DropDownListFor(model => model.Year, Model.YearList, new { AutoPostBack = "true"})
<input type="submit" name="name" value="Submit" />
}
<table id="thetable">
<thead>
//some headers
</thead>
<tbody>
#foreach ( var item in Model.Data)
{
//row for each data
}
</tbody>
</table>
As you can see I'm hoping for the Year property to be updated and a new call to be made for the Data property which would result in new information. That information would be rendered in thetable Table
Here's a full example.
Model:
public class MyViewModel
{
public int Year { get; set; }
public IEnumerable<SelectListItem> Years
{
get
{
return Enumerable.Range(1980, 40).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
}
}
public IList<TheData> Data { get; set; }
}
public class TheData
{
public int Year { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(int year)
{
var model = new[]
{
new TheData { Year = year, Foo = "foo 1", Bar = "bar 1" },
new TheData { Year = year, Foo = "foo 2", Bar = "bar 2" },
new TheData { Year = year, Foo = "foo 3", Bar = "bar 3" },
};
return PartialView("_data", model);
}
}
Index.cshtml view:
#model MyViewModel
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$('#yearsddl').change(function () {
$(this).closest('form').trigger('submit');
});
});
</script>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "data" }))
{
#Html.DropDownListFor(x => x.Year, Model.Years, new { id = "yearsddl" })
}
<table>
<thead>
<tr>
<th>Year</th>
<th>Foo</th>
<th>Bar</th>
</tr>
</thead>
<tbody id="data">
#Html.Partial("_data", Model.Data ?? Enumerable.Empty<TheData>())
</tbody>
</table>
The jquery.unobtrusive-ajax.js script inclusion should be moved out of the index view inside the layout for example and the custom js that subscribes for the change event of the dropdownlist should be moved into a separate js file and included from the Layout. I just put them here to illustrate a full example of what's required for the view to work.
_Data.cshtml partial:
#model IList<TheData>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.DisplayFor(x => x[i].Year)</td>
<td>#Html.DisplayFor(x => x[i].Foo)</td>
<td>#Html.DisplayFor(x => x[i].Bar)</td>
</tr>
}

Populating child drop down based on what was selected from parent drop down list

I am using the latest version of jQuery and ASP.NET MVC 3 with the Razor view engine.
I have tried Google looking for a decent example of loading a child drop down list when a parent drop down item is selected. I am looking to do this via jQuery AJAX using JSON. My knowledge of this is zero.
I have a Category class with a list of categories. It's a parent-child association.
If I select a category from the parent drop down list, then all the child categories need to be listed in the child drop down list for the selected parent category.
This is what I currently have, but need to complete it, not sure if I am in the right direction:
$(document).ready(function () {
$('#ddlParentCategories').change(function () {
alert('changed');
});
});
I loaded my drop down list from my view model as such:
#Html.DropDownListFor(x => x.ParentCategoryId, new SelectList(Model.ParentCategories, "Id", "Name", Model.ParentCategoryId), "-- Select --", new { id = "ddlParentCategories" })
The first item has text "-- Select --" (for both parent and child drop down lists). On initial page load nothing must be loaded in the child drop down list. When a value is selected then the child drop down list must be populated. And when "-- Select --" is selected again in the parent drop down list then all the items in the child drop down list must cleared except "-- Select --".
If possible, if the child categories is loading, how do I display that "round" loading icon?
UPDATE
I have updated my code to Darin's code, and I cannot get it to work properly:
Category class:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public bool IsActive { get; set; }
public int? ParentCategoryId { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> ChildCategories { get; set; }
}
EditProductViewModel class:
public class EditProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public bool IsActive { get; set; }
public string PageTitle { get; set; }
public bool OverridePageTitle { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public int ParentCategoryId { get; set; }
public IEnumerable<Category> ParentCategories { get; set; }
public int ChildCategoryId { get; set; }
public IEnumerable<Category> ChildCategories { get; set; }
}
ProductController class:
public ActionResult Create()
{
EditProductViewModel viewModel = new EditProductViewModel
{
ParentCategories = categoryService.GetParentCategories()
.Where(x => x.IsActive)
.OrderBy(x => x.Name),
ChildCategories = Enumerable.Empty<Category>(),
IsActive = true
};
return View(viewModel);
}
public ActionResult AjaxBindingChildCategories(int parentCategoryId)
{
IEnumerable<Category> childCategoryList = categoryService.GetChildCategoriesByParentCategoryId(parentCategoryId);
return Json(childCategoryList, JsonRequestBehavior.AllowGet);
}
Create view:
<tr>
<td><label>Parent Category:</label> <span class="red">*</span></td>
<td>#Html.DropDownListFor(x => x.ParentCategoryId,
new SelectList(Model.ParentCategories, "Id", "Name", Model.ParentCategoryId),
"-- Select --",
new { data_url = Url.Action("AjaxBindingChildCategories"), id = "ParentCategories" }
)
#Html.ValidationMessageFor(x => x.ParentCategoryId)
</td>
</tr>
<tr>
<td><label>Child Category:</label> <span class="red">*</span></td>
<td>#Html.DropDownListFor(x => x.ChildCategoryId,
new SelectList(Model.ChildCategories, "Id", "Name", Model.ChildCategoryId),
"-- Select --",
new { id = "ChildCategories" }
)
#Html.ValidationMessageFor(x => x.ChildCategoryId)
</td>
</tr>
<script type="text/javascript">
$(document).ready(function () {
$('#ParentCategories').change(function () {
var url = $(this).data('url');
var data = { parentCategoryId: $(this).val() };
$.getJSON(url, data, function (childCategories) {
var childCategoriesDdl = $('#ChildCategories');
childCategoriesDdl.empty();
$.each(childCategories, function (index, childCategory) {
childCategoriesDdl.append($('<option/>', {
value: childCategory, text: childCategory
}));
});
});
});
});
</script>
It goes into my AjaxBindingChildCategories action and it brings back records, it just doesn't want to display my child category dropdownlist. I had a look in Fire Bug and the error that I get is:
GET AjaxBindingChildCategories?parentCategoryId=1
500 Internal Server Error
Here's an example of cascading drop down lists. As always start by defining a view model:
public class MyViewModel
{
[DisplayName("Country")]
[Required]
public string CountryCode { get; set; }
public IEnumerable<SelectListItem> Countries { get; set; }
public string City { get; set; }
public IEnumerable<SelectListItem> Cities { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fetch countries from somewhere
Countries = new[]
{
new SelectListItem { Value = "FR", Text = "France" },
new SelectListItem { Value = "US", Text = "USA" },
},
// initially we set the cities ddl to empty
Cities = Enumerable.Empty<SelectListItem>()
};
return View(model);
}
public ActionResult Cities(string countryCode)
{
// TODO: based on the selected country return the cities:
var cities = new[]
{
"Paris", "Marseille", "Lyon"
};
return Json(cities, JsonRequestBehavior.AllowGet);
}
}
a view:
#model MyViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.CountryCode)
#Html.DropDownListFor(
x => x.CountryCode,
Model.Countries,
"-- Select country --",
new { data_url = Url.Action("cities") }
)
#Html.ValidationMessageFor(x => x.CountryCode)
</div>
<div>
#Html.LabelFor(x => x.City)
#Html.DropDownListFor(
x => x.City,
Model.Cities,
"-- Select city --"
)
#Html.ValidationMessageFor(x => x.City)
</div>
<p><input type="submit" value="OK" /></p>
}
and finally the unobtrusive javascript in a separate file:
$(function () {
$('#CountryCode').change(function () {
var url = $(this).data('url');
var data = { countryCode: $(this).val() };
$.getJSON(url, data, function (cities) {
var citiesDdl = $('#City');
citiesDdl.empty();
$.each(cities, function (index, city) {
citiesDdl.append($('<option/>', {
value: city,
text: city
}));
});
});
});
});
jQuery script will look like this:
<script type="text/javascript">
function getCities(abbr) {
$.ajax({
url: "#Url.Action("Cities", "Locations")",
data: {abbreviation: abbr},
dataType: "json",
type: "POST",
error: function() {
alert("An error occurred.");
},
success: function(data) {
var items = "";
$.each(data, function(i, item) {
items += "<option value=\"" + item.Value + "\">" + item.Text + "</option>";
});
$("#City").html(items);
}
});
}
$(document).ready(function(){
$("#State").change(function() {
var abbr = $("#State").val();
getCities(abbr);
});
});
</script>
A repository to retrieve data could look like this (obviously hook it up to live data though):
public class LocationRepository : ILocationRepository
{
public IQueryable<State> GetStates()
{
return new List<State>
{
new State { Abbreviation = "NE", Name = "Nebraska" },
new State { Abbreviation = "NC", Name = "North Carolina" }
}.AsQueryable();
}
public IQueryable<City> GetCities(string abbreviation)
{
var cities = new List<City>();
if (abbreviation == "NE")
{
cities.AddRange(new List<City> {
new City { Id = 1, Name = "Omaha" },
new City { Id = 2, Name = "Lincoln" }
});
}
else if (abbreviation == "NC")
{
cities.AddRange(new List<City> {
new City { Id = 3, Name = "Charlotte" },
new City { Id = 4, Name = "Raleigh" }
});
}
return cities.AsQueryable();
}
}
public interface ILocationRepository
{
IQueryable<State> GetStates();
IQueryable<City> GetCities(string abbreviation);
}
Controllers could look like this:
public class LocationsController : Controller
{
private ILocationRepository locationRepository = new LocationRepository();
[HttpPost]
public ActionResult States()
{
var states = locationRepository.GetStates();
return Json(new SelectList(state, "Id", "Name"));
}
[HttpPost]
public ActionResult Cities(string abbreviation)
{
var cities = locationRepository.GetCities(abbreviation);
return Json(new SelectList(cities, "Abbreviation", "Name"));
}
}
I am assuming you populate the parent dropdown from server side and also the first option in this parent dd is "--- Select ---"
You can try this
$(document).ready(function () {
var $parent = $('#ddlParentCategories');
var $child = $('#ddlChildCategories');
$child.find("option:gt(0)").remove();
if(!$parent.children().eq(0).is(":selected")){
$parent.change(function () {
$.ajax({
url: "urlToFetchTheChild",
data: { categoryId: this.value },
success: function(data){
//The data you send should be a well formed array of json object containing code/value pair of child categories
for(var i = 0;i<data.length;i++){
$child.append("<option value='"+ data[i].code +"'>"+ data[i].value +"</option>");
}
}
});
});
}
});

Resources