DropDownList with possible nested DropDownList in MVC4 - asp.net-mvc-4

I have a set of questions the user can choose from and some of those questions have a secondary list of options to choose from. My goal is to have a drop down list and if you pick one of the options that has items in its SecondaryChoiceList then a second list would appear below the initial dropdown and all of this would be strongly typed and bound to the model upon submission.
I can get the initial list to appear by saying:
#Html.DropDownListFor( x => x.SelectedChoiceId, new SelectList(Model.Choices, "Id", "Name"))
But that has no hooks to the secondary list and I am completely lost as to how I would tie that secondary list back to the model that is returned when I submit the form.
Here's my view model:
public class ExampleViewModel
{
public List<Choice> ChoiceList { get; set; }
public int SelectedChoiceId { get; set; }
public int SelectedAffiliateId { get; set; }
}
Here is what a Choice looks like:
public class Choice
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<SecondaryChoice> SecondaryChoiceList { get; set; }
public Choice()
{
SecondaryChoiceList = new List<SecondaryChoice>();
}
}
And here is my SecondaryChoice object:
public class EligibleAffiliate
{
public int Id { get; set; }
public int EligibilityChoiceId { get; set; }
public string Name { get; set; }
}
If there is anything that I can clear up let me know.

I have tried to keep it as simple as possible.
So, a sample model is given below:
namespace StackOverflow.Models
{
public class Choice
{
public int Id { get; set; }
public string Name { get; set; }
public Choice()
{
Id = 0;
}
public Choice(int id, string name)
{
Id = id;
Name = name;
}
}
}
namespace StackOverflow.Models
{
public class ExampleViewModel
{
public List<Choice> PrimaryChoiceList { get; set; }
public List<Choice> SecondaryChoiceList { get; set; }
public int SelectedChoiceId { get; set; }
public int SelectedAffiliateId { get; set; }
public ExampleViewModel()
{
SelectedChoiceId = 0;
SelectedAffiliateId = 0;
PrimaryChoiceList = new List<Choice>()
{
new Choice(1, "How are you?"),
new Choice(2, "How is the weahter?"),
new Choice(3, "What have you been doing so far?"),
new Choice(4, "What's up man?"),
new Choice(5, "Any news?"),
new Choice(5, "Bla bla bla")
};
SecondaryChoiceList = new List<Choice>()
{
new Choice(1, "How are you dear?"),
new Choice(2, "How is the weahter?"),
new Choice(3, "What have you been doing so far dear?"),
new Choice(4, "What's up man?"),
new Choice(5, "Any romantic news?")
};
}
}
}
Sample controller:
namespace StackOverFlow.Controllers
{
public class SOController : Controller
{
public static ExampleViewModel evm = new ExampleViewModel();
public ActionResult Index()
{
return View(evm);
}
public ActionResult SetSelection(int id)
{
evm.SelectedChoiceId = id;
if (evm.PrimaryChoiceList.Count() > 0)
{
Choice selection = evm.PrimaryChoiceList.ElementAt(id-1);
Choice affiliate = (Choice)evm.SecondaryChoiceList.FirstOrDefault(x => x.Name == selection.Name);
if (affiliate != null)
{
return Content("show");
}
else
{
return Content("hide");
}
}
else
{
return Content("hide");
}
}
}
}
And the web page:
#using StackOverflow2.Models;
#model ExampleViewModel
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
#{
ViewBag.Title = "Stackoverflow Sample";
}
<h2>Index</h2>
<script type="text/javascript">
// Get the selection and make Ajax Request to the controller, action: SetSelection,
// which in turn may decide whetger you must show or hide the control
function updateSeconadryQuestion(id) {
var xmlhttp;
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
if (xmlhttp.responseText == 'show')
$('#SecondaryQuestionDropBoxId').show();
else
$('#SecondaryQuestionDropBoxId').hide();
}
}
xmlhttp.open("GET", "/SO/SetSelection?id=" + id, true);
xmlhttp.send();
}
</script>
#Html.DropDownListFor(x => x.SelectedChoiceId, new SelectList(Model.PrimaryChoiceList, "Id", "Name", "Value"), new { id = "PrimaryQuestionDropBoxId", onchange = "updateSeconadryQuestion(value);" })
<div id="SeconadryQuestionDivId">
#Html.DropDownListFor(x => x.SelectedAffiliateId, new SelectList(Model.SecondaryChoiceList, "Id", "Name"), new { id = "SecondaryQuestionDropBoxId" })
</div>

Related

How to re-Render Blazor Component witch Render by RenderFragment or re-Bind Resource for Child who Render by RenderFragment

Question :
I am looking for how to let "RenderFragment" can re-Render or re-Binding Resource .
Environment:
VS2019 Preview , .net core 6 preview (I think it is not different with 5)
What I Had Try :
My Razor Component page has a selection and a button ,
Selection will get default resource when page first render ,
Button Click Cvent should change the resource of Selection .
This is What I prefer for correct result
But Actually ,
Resource is not changing when I Using RenderFragment to create component .
Resource is not changing.
I Stop at FabArea.razor to check if button click changed oResource .
Stop at Button Clicked.
I tried to add StateHasChanged() in my code , but it didn't help when oResource has changed .
oResource Changed into B , by Selection's Resource is still same.
In some reason I have to make each "Area" ,"selection" and "button" into different component .
Here is My code .
Parent Component : DyResource.Razor
#page "/DyResource"
<h3>DyResource</h3>
<p>You Just Select : #SelectedValue </p>
<p>isDefault Resource : #(isDefaultResource?"Default Value":"Not Default Value") </p>
<p>Resource[0]: #(oResource.First().id.ToString())</p>
<FabArea Componets="#liComponets"></FabArea>
#code {
public List<iFabComponet> liComponets { get; set; }
public List<Selection> oResource { get; set; }
public string SelectedValue { get; set; }
public bool isDefaultResource { get; set; } = true;
public class Selection
{
public string id { get; set; }
public string text { get; set; }
}
public class iFabComponet
{
public Type Type { get; set; }
public string Row { get; set; }
public string Length { get; set; }
public string Seq { get; set; }
public RenderFragment Control { get; set; }
public Dictionary<string, object> Dic { get; set; }
public object TComponent { get; set; }
}
protected override void OnInitialized()
{
DefaultResource(); //Set oResource
CreateComponent();
}
//Selection Resource When First Render Pages
private void DefaultResource()
{
oResource = new List<Selection>
{
new Selection { id = "A01", text = "A01" },
new Selection { id = "A02", text = "A02" },
new Selection { id = "A03", text = "A03" },
new Selection { id = "A04", text = "A04" },
new Selection { id = "A05", text = "A05" }
};
//StateHasChanged();
}
// Selection Resource When Button Click
private void ChangedResource()
{
oResource = new List<Selection>()
{
new Selection { id = "B01", text = "B01" },
new Selection { id = "B02", text = "B02" },
new Selection { id = "B03", text = "B03" },
new Selection { id = "B04", text = "B04" },
new Selection { id = "B05", text = "B05" }
};
//StateHasChanged();
}
//Create KeyValuePair for Child Components
private void CreateComponent()
{
List<iFabComponet> FCs = new List<iFabComponet>();
var DDLDic = new Dictionary<string, object>();
DDLDic.Add("Label", "Type");
DDLDic.Add("Resource", oResource);
DDLDic.Add("TextField", "id");
DDLDic.Add("ValueField", "text");
DDLDic.Add("Enabled", true);
DDLDic.Add("Id", "ddlType");
DDLDic.Add("Width", "100%");
DDLDic.Add("ResultValueChanged", EventCallback.Factory.Create<System.String>(this, str => TypeSelected(str)));
iFabComponet FirstCom = new iFabComponet() { Type = typeof(FabDDL<string, Selection>), Row = "1", Length = "6", Seq = "1", Dic = DDLDic };
liComponets.Add(FirstCom);
var btnDic = new Dictionary<string, object>();
btnDic.Add("ButtonTitle", "Get Data");
btnDic.Add("isNeedPad", true);
btnDic.Add("PadLength", 4);
btnDic.Add("OnClick", EventCallback.Factory.Create<System.String>(this, str => BtnClick()));
iFabComponet SecCom = new iFabComponet() { Type = typeof(FabButton), Row = "2", Length = "6", Seq = "2", Dic = btnDic };
liComponets.Add(SecCom);
}
//Selection Event
private void TypeSelected(string x)
{
SelectedValue = x;
}
//Button Event
private void BtnClick()
{
if (isDefaultResource)
{
ChangedResource();
isDefaultResource = false;
}
else
{
DefaultResource();
isDefaultResource = true;
}
CreateComponent();
StateHasChanged();
}
}
FabArea.Razor (Area to show and Render Child Component)
#using System.Linq.Expressions
<div class="card">
<div class="card-body">
#foreach (var item in Contents)
{
#item
;
}
</div>
</div>
#code {
[Parameter]
public List<iFabComponet> Componets { get; set; }
public List<RenderFragment> Contents { get; set; }
protected override void OnInitialized()
{
if (Componets.Count() > 0 && Componets != null)
{
CreateFragment();
}
}
public async void CreateFragment()
{
int iComponent = 0;
List<RenderFragment> RFTs = new List<RenderFragment>();
Contents = new List<RenderFragment>();
int iContent = 1;
foreach (var area in Componets)
{
RenderFragment renderFragment = (builder) =>
{
builder.OpenComponent(iComponent, area.Type);
//Using For Checking Resource .
foreach (var item in area.Dic)
{
var q = item.Key;
var w = item.Value;
}
builder.AddMultipleAttributes(iContent, area.Dic);
builder.CloseComponent();
};
Contents.Add(renderFragment);
}
StateHasChanged();
}
}
Here is Selection Component and Button Component .
FabDDL.razor
#typeparam T
#typeparam TResource
<div class="row">
<label class="col-md-2">#Label</label>
<div class="col-md-5">
<select class="selection" id="#id" disabled="#(!Enabled)" #onchange="#(() => ResultValueChanged.InvokeAsync())">
<option></option>
#if (Resource != null)
{
#foreach (var item in Resource)
{
<option value="#(item.GetType().GetProperty(ValueField).GetValue(item))">#(item.GetType().GetProperty(TextField).GetValue(item))</option>
}
}
</select>
</div>
</div>
#code {
[Parameter] public string Label { get; set; }
//[Parameter] public T ResultValue { get; set; }
[Parameter] public List<TResource> Resource { get; set; }
[Parameter] public string DefaultText { get; set; } = "Select an Option";
[Parameter] public string id { get; set; } = "DropDownList" + Guid.NewGuid().ToString();
[Parameter] public string Width { get; set; } = "100 %";
[Parameter] public bool Enabled { get; set; }
[Parameter] public string TextField { get; set; }
[Parameter] public string ValueField { get; set; }
[Parameter] public EventCallback<T> ResultValueChanged { get; set; }
}
<style>
.selection {
width: 100%;
height: 100%;
padding-left: 15px;
}
</style>
FabButton.razor
#if (isNeedPad){<div class="#PadRowClass"></div>}
<div class="col-md-2 pt-3 middle">
<button class="btn btn-info" type="#ButtonType" #onclick="#(() => OnClick.InvokeAsync())">#ButtonTitle</button>
</div>
#code {
[Parameter] public string ButtonTitle { get; set; } = "Click ME!";
[Parameter] public string ButtonType { get; set; } = "button";
[Parameter] public EventCallback<string> OnClick { get; set; }
[Parameter] public bool isNeedPad { get; set; } = false;
[Parameter] public int PadLength { get; set; } = 1;
public string PadRowClass { get; set; }
protected override void OnInitialized()
{
PadRowClass = "col-md-" + PadLength.ToString();
base.OnInitialized();
}
}

Retrieve values from SQL database - EF

I'm trying to figure out how to pull values from a SQL database and display this in a razor view.
I have the following class using Entity Framework (I believe)
public class EventLog
{
[Key]
public int Id { get; set; }
public int EventId { get; set; }
public int MaxDelegates { get; set; }
public string Code { get; set; }
public DateTime End { get; set; }
public string Title { get; set; }
}
And I want to map title to DBTitle in the following model:
public class CourseDetailVM : CourseDetailSummaryVM
{
public EventLog DBTitle { get; set; }
}
I then want to see this in the following view:
#using TSW.Web.Helpers
#model TSW.Web.ViewModels.CourseDetailVM
#{
Layout = "~/Views/_Master.cshtml";
}
#Model.DBTitle.Title;
I have the following controller already in place (sorry for the length I plan to reduce this down):
public class CourseDetailController : BaseRenderController<CourseDetailPageDT>
{
private readonly ISitePageFactory _pageFactory = null;
private readonly IEventService _eventService = null;
public CourseDetailController(IEventService eventService, ISitePageFactory pageFactory)
{
_pageFactory = pageFactory;
_eventService = eventService;
}
public async Task<ActionResult> CourseDetail()
{
var homepage = _pageFactory.GetCurrentHomepage();
var model = Mapper.Map<CourseDetailVM>(CurrentContent);
model.Email = homepage.ContactEmail;
model.PhoneNumber = homepage.HeaderPhoneNumber;
model.InnerPageHeader.ShowHeading = true;
model.InnerPageHeader.Title = model.PageTitle;
if (model.Categories.Count == 1)
{
var categoryTagId = model.Categories.First().Id;
var contentTypeAlias = DocumentTypeHelper.GetDocumentTypeAlias<CourseListingPageDT>();
var courseCategoryPage = Umbraco.TypedContentAtXPath($"//{contentTypeAlias}")
.FirstOrDefault(x => x.GetPropertyValue<int>(Constants.DocumentTypes.CourseListingPage.Category) == categoryTagId);
if (courseCategoryPage != null)
{
model.InnerPageHeader.BackLink = Mapper.Map<LinkItem>(courseCategoryPage.Id);
}
}
try
{
model.Events = await _eventService.GetEventsForCourse(CurrentContent.AdministrateId);
}
catch (Exception ex)
{
model.Events = new StaticPagedList<Event>(Enumerable.Empty<Event>(), 1, 1, 0);
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
if (CurrentContent.Graphic != 0)
{
model.InnerPageHeader.Graphic = Mapper.Map<CtaItem>(CurrentContent.Graphic);
}
return View(model);
}
}
I've tried every suggestion I can google to add the mapping in the controlling but can't get my head around this simple function of pulling the value from a SQL database into the razor view.
Could anyone help me out?

Asp.net MVC , CheckBoxs not binding in case of list

Though it seems like it has been discussed earlier also , but I could not find the solution -:
I have a VM like this-:
public class RoomsVm
{
public RoomsVm()
{
Rooms.Add(new Room()
{
IsVip = true,Id = "1"
}
);
Rooms.Add(new Room()
{
IsVip = false,
Id = "2"
}
);
}
public List<Room> Rooms=new List<Room>();
}
public class Room
{
public bool IsVip { get; set; }
public string Id { get; set; }
}
My controller has 2 simple actions- 1- for get and 2 for post-:
public ActionResult IndexView2()
{
var roomVM = new RoomsVm();
return View(roomVM);
}
[HttpPost]
public ActionResult IndexView2(RoomsVm roomsVm)
{
return View(roomsVm);
}
In my view - I simply display my rooms with check boxes like this-:
#model MVCApplication.Models.RoomsVm
<h2>IndexView2</h2>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Rooms.Count(); i++)
{
#Html.CheckBoxFor(modelItem=>modelItem.Rooms[i].IsVip,new{value=Model.Rooms[i].Id})
}
<input type="submit" value="Submit"/>
}
Problem- When I click Submit button, I don't get updated values of rooms checkboxes in input value of Post action ..
What wrong am I doing ?
You don't have accessors on your Rooms property
public class RoomsVm
{
public RoomsVm()
{
Rooms = new List<Room>(); // Initialize rooms
Rooms.Add(new Room() {.... //Add rooms
}
public List<Room> Rooms { get; set; } // Add get/set
}

"inline" editing in a mvc 4 list of objects

I have a strange problem and I don't know if this is actually possible.
What I want is, to be able to list all the values from my model and and edit them directly in the list.
Here's what I have:
Model Linker:
public class StoreLinkerModel
{
//public Guid? id { get; set; }
public IEnumerable<Stores> StoresAndOpeninghours { get; set; }
}
public class Stores
{
public long ID { get; set; }
public string StoreName { get; set; }
public string Address { get; set; }
public string Zip { get; set; }
public string City { get; set; }
}
My Controller:
public ActionResult Overview()
{
var model = new StoreLinkerModel
{
StoresAndOpeninghours = new[]
{
new Stores()
{
ID = 0,
Address = "Enghavevej 70"
},
new Stores()
{
ID=1,
Address = "Løngangsgade 30"
},
}
};
return View(model);
}
[HttpPost]
public ActionResult Overview(StoreLinkerModel model)
{
if (ModelState.IsValid)
{
var x = "go go go";
}
return RedirectToAction("Overview");
}
My overview.aspx page:
#model streetoffrs.web.Models.StoreLinkerModel
#{
ViewBag.Title = "Overview";
Layout = "~/Views/Shared/_dashboard.cshtml";
}
#Html.EditorFor(x => x.StoresAndOpeninghours)
and my EditorTemplate stores.aspx
#model streetoffrs.web.Models.Stores
#using (Html.BeginForm("Overview", "Dashboard", FormMethod.Post, new { name = "id" + #Html.DisplayFor(m => m.ID) }))
{
#Html.EditorFor(x => x.Address)
<input type="submit" class="left btn btn-primary" value="Ret butiksdata">
}
<br />
The list is being generated as it should, and when I hit the first button at the first editorfor it will post the model to my controller, but when I push the 2nd button, the model is null, but the first button still works!
Is this possible at all, if yes what am I missing, if not, tell me how I can accomplish this.
thanks in advance!
you need edit post action like this:
[HttpPost]
public ActionResult Overview(StoreLinkerModel model)
{
if (ModelState.IsValid)
{
var x = "go go go";
}
return View(model);
}
the RedirectToAction will be go to the first Overview Action,so you will be lost the data.

Range Validation is not working

I have used range validation but this is not working. I am adding model,controller and view code.Please help(i have added only related fields only in this code).
Model is :
public class TicketDetailModel : TicketModelBase
{
public WorkOnTicketCreateModel WorkOnTicketCreateModel { get; set; }
}
public class TicketModelBase
{
[Required]
[Range(1, int.MaxValue, ErrorMessage = "Please enter a value bigger than {0}")]
public int EstimatedTime { get; set; }
public virtual List<WorkOnTicket> WorkOnTickets { get; set; }
}
public class WorkOnTicketCreateModel : WorkOnTicketModelBase
{
[Required]
[Display(Name = "AssignedToUser")]
public int AssignedToUserId { get; set; }
public IEnumerable<SelectListItem> AssignedUser { get; set; }
[Required]
[Display(Name = "Ticket Status")]
public int TicketStatusId { get; set; }
public TicketStatus TicketStatusVal { get; set; }
public IEnumerable<SelectListItem> TicketStatus { get; set; }
}
public class WorkOnTicketModelBase
{
public int Id { get; set; }
[Required]
public int EstimateHours { get; set; }
[Required]
[Range(1, int.MaxValue, ErrorMessage = "Please enter a value bigger than {0}")]
public int WorkedHours { get; set; }
}
Contoller:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Details(TicketDetailModel model, IEnumerable<HttpPostedFileBase> file)
{
using (ITransaction transaction = this.nhSession.BeginTransaction())
{
var ticketObj = this.nhSession.QueryOver<Ticket>().Where(t => t.Id == model.Id).SingleOrDefault();
var workOnTicket = new WorkOnTicket();
workOnTicket.Ticket = ticketObj;
workOnTicket.WorkedHours = model.WorkOnTicketCreateModel.WorkedHours;
workOnTicket.EstimateHours = model.WorkOnTicketCreateModel.EstimateHours;
ticketObj.WorkOnTickets.Add(workOnTicket);
this.nhSession.Save(ticketObj);
transaction.Commit();
}
return RedirectToAction("Details", new { id = model.Id, milestoneId = model.Milestone.Id, projectId = model.Project.Id });
}
View:-
#model AnkTech.TicketManagement.Web.Models.Ticket.TicketDetailModel
#using (Html.BeginForm("Details", "Ticket", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
#Html.TextBoxFor(model => model.WorkOnTicketCreateModel.EstimateHours, new { #id = "work_remaining", #class = "s-mini", size = "2" })
Worked hours: #Html.TextBoxFor(model => model.WorkOnTicketCreateModel.WorkedHours, new { #id = "worked_hours", #class = "s-mini", size = "2" })
<input type="submit" value="Submit" tabindex="2" name="commit" id="submit-comment"
class="gray-btn">
}
I have deleted all rmaining fields. i have added only fields to which related i am asking, please help.
You need to use ModelState.IsValid to check that the model is actually valid. Currently you assign validation attributes but never check them:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Details(TicketDetailModel model, IEnumerable file) {
if (!ModelState.IsValid)
{
// Handle error
}
else
{
using (ITransaction transaction = this.nhSession.BeginTransaction()) {
var ticketObj = this.nhSession.QueryOver<Ticket>().Where(t => t.Id == model.Id).SingleOrDefault();
var workOnTicket = new WorkOnTicket();
workOnTicket.Ticket = ticketObj;
workOnTicket.WorkedHours = model.WorkOnTicketCreateModel.WorkedHours;
workOnTicket.EstimateHours = model.WorkOnTicketCreateModel.EstimateHours;
ticketObj.WorkOnTickets.Add(workOnTicket);
this.nhSession.Save(ticketObj);
transaction.Commit();
}
}
return RedirectToAction("Details", new { id = model.Id, milestoneId = model.Milestone.Id, projectId = model.Project.Id });
}