Umbraco: Create CheckBoxList property with prevalues from mvc model - asp.net

What I want to do is create a CheckBoxList property so the editor could choose facilities specific for current page (hotel name) in BO, and render content based on what is checked.
I've created a model:
public class Facility
{
public int Id { get; set; }
public string Description { get; set; }
public string IconUrl { get; set; }
public List<Facility> GetFacilities()
{
return new List<Facility>()
{
new Facility() { Id = 4, Description = "Free parking", IconUrl = "" },
new Facility() { Id = 6, Description = "Spa", IconUrl = "" },
new Facility() { Id = 7, Description = "Free Wifi", IconUrl = "" },
new Facility() { Id = 2, Description = "Tennis", IconUrl = "" },
new Facility() { Id = 9, Description = "Room service", IconUrl = "" },
new Facility() { Id = 10, Description = "Fitness", IconUrl = "" }
};
}
}
How can I create a CheckBoxList with the values set in GetFacilities() method? Or should I create a new class in AppCode folder with this method? Where is the best place to put this kind of functionality, and how can I achieve this?

Your Facility model should contain a boolean value to indicate if its been selected
public class FacilityVM
{
public int Id { get; set; }
public string Description { get; set; }
public bool IsSelected { get; set; }
{
public class HotelVM
{
public int ID{ get; set; }
....
public List<FacilityVM> Facilities { get; set; }
}
Controller
public ActionResult Edit(int ID)
{
HotelVM model = new HotelVM();
model.Facilities = // populate the list of available facilities
// Get the hotel from repository and map properties to the view model
return View(model);
}
public ActionResult Edit(HotelVM model)
{
...
foreach(FacilityVM facility in model.Facilities)
{
if (facility.IsSelected)
{
// do something
}
}
....
}
View
#model HotelVM
#using (Html.BeginForm())
{
// render properties of hotel
....
for (int i = 0; i < Model.Facilities.Count; i++)
{
#Html.HiddenFor(m => m.Facilities[i].ID);
#Html.HiddenFor(m => m.Facilities[i].Description);
#Html.CheckBoxFor(m => m.Facilities[i].IsSelected)
#Html.LabelFor(m => m.Facilities[i].IsSelected, Model.Facilities[i].Description)
}
<input type="submit" value="Save" />
}

I think you're thinking about this the wrong way as suggested by Stephen (unless I am misunderstanding your question). You are creating a list of key/value pairs and only one will be selected in the BO and so only one will published to the front-end (regardless of the use of it).
So, in the BO you only need a dropdown list with the key/values pairs. You can create this with the "Dropdown list (publishing keys)" datatype. Also consider using the "SQL dropdown" list datatype as this would give you far more flexibility.
If you then need to convert the selected ID into a Facility object, do this separately using a class implementing the IPropertyEditorValueConverter interface. See here for more information:
http://our.umbraco.org/documentation/extending-umbraco/Property-Editors/PropertyEditorValueConverters

Related

ASP.Net Core MVC - List<SelectListItem> multiselect not showing select = true items

I'm making a strongly typed update/create view for "Medical Supplies" in which users can select multiple options ("Kits") from a dropdown list that received a List<SelectListItem>. The list is a <select multiple="multiple">. It was originally working perfectly, but I must have accidentally changed something.
Now the dropdown does not display SelectListItems passed to it as selected = true as selected (as verified by VS debugger), so I can select new items but not deselect previously selected ones. I need this to compare the list of IDs from the new selection to the old one in order to determine what must be removed from the Db.
This is my view model:
public class MedicalSupplyViewModel
{
public MedicalSupplyViewModel()
{
Supply = new MedicalSupply();
KitList = new List<SelectListItem>();
KitIds = new List<int>();
}
public MedicalSupply Supply { get; set; }
public List<SelectListItem> KitList { get; set; }
public string StringKits { get; set; }
public List<int> KitIds { get; set; }
public void KitStringSet()
{
IEnumerable<string> KitNames = Supply.KitSupplies.Select(ks => ks.Kit.Name);
StringKits = Supply.KitSupplies.Count() == 0 ? "N/A" : string.Join(", " , KitNames);
}
}
This is the relevant cshtml in my view:
<select multiple="multiple" class="form-control" id="kit_select" asp-for="KitIds" asp-items="Model.KitList"></select>
This is the part of the controller method for this page that creates the SelectListItems:
DetailsModel.KitList = _db.Kits.ToList().ConvertAll(k =>
{
return new SelectListItem()
{
Value = k.Id.ToString(),
Text = k.Name,
Selected = SelectedKits.Contains(k)
};
});
Even setting the item to selected = true will not display them as such. I've set breakpoint everywhere, including in the view, and I cannot find a discrepancy between the selected property and what it should be anywhere except for the rendered html. I've also used different browsers and spent hours searching the internet and this website.
What could be the cause of this issue?
You should set the selected items' value to the KitIds. Below is my test example:
Model:
public class MedicalSupplyViewModel
{
public MedicalSupplyViewModel()
{
KitList = new List<SelectListItem>();
KitIds = new List<int>();
}
public List<SelectListItem> KitList { get; set; }
public string StringKits { get; set; }
public List<int> KitIds { get; set; }
}
public class Kit
{
public int Id { get; set; }
public string Name { get; set; }
}
Controller:
public IActionResult Index()
{
List<Kit> kits = new List<Kit>
{
new Kit{ Id = 1, Name = "AA"},
new Kit{ Id = 2, Name = "BB"},
new Kit{ Id = 3, Name = "CC"},
};
List<Kit> SelectedKits = new List<Kit>
{
new Kit{ Id = 1, Name = "AA"},
new Kit{ Id = 2, Name = "BB"}
};
var DetailsModel = new MedicalSupplyViewModel();
DetailsModel.KitIds = SelectedKits.Select(x => x.Id).ToList();
DetailsModel.KitList = kits.ToList().ConvertAll(k =>
{
return new SelectListItem()
{
Value = k.Id.ToString(),
Text = k.Name
};
});
return View(DetailsModel);
}
View:
<select multiple="multiple" class="form-control" id="kit_select" asp-for="KitIds" asp-items="Model.KitList"></select>
Result:

ASP.Net MVC : Binding Dropdownlist to a List on the Model [duplicate]

I'm developing an ASP.NET MVC 5 application, with C# and .NET Framework 4.6.1.
I have this View:
#model MyProject.Web.API.Models.AggregationLevelConfViewModel
[...]
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, (SelectList)Model.HelperCodeTypeItems, new { id = "Configurations[0].HelperCodeType" })
The ViewModel is:
public class AggregationLevelConfViewModel
{
private readonly List<GenericIdNameType> codeTypes;
private readonly List<GenericIdNameType> helperCodeTypes;
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
public int ProductionOrderId { get; set; }
public string ProductionOrderName { get; set; }
public IList<Models.AggregationLevelConfiguration> Configurations { get; set; }
public AggregationLevelConfViewModel()
{
// Load CodeTypes to show it as a DropDownList
byte[] values = (byte[])Enum.GetValues(typeof(CodeTypes));
codeTypes = new List<GenericIdNameType>();
helperCodeTypes = new List<GenericIdNameType>();
for (int i = 0; i < values.Length; i++)
{
GenericIdNameType cType = new GenericIdNameType()
{
Id = values[i].ToString(),
Name = EnumHelper.GetDescription((CodeTypes)values[i])
};
if (((CodeTypes)values[i]) != CodeTypes.NotUsed)
codeTypes.Add(cType);
helperCodeTypes.Add(cType);
}
}
}
And Models.AggregationLevelConfiguration is:
public class AggregationLevelConfiguration
{
public byte AggregationLevelConfigurationId { get; set; }
public int ProductionOrderId { get; set; }
public string Name { get; set; }
public byte CodeType { get; set; }
public byte HelperCodeType { get; set; }
public int PkgRatio { get; set; }
public int RemainingCodes { get; set; }
}
I need to set selected value in these properties:
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
But I can't set it in new SelectList(codeTypes, "Id", "Name"); or new SelectList(helperCodeTypes, "Id", "Name"); because the selected value are in Configurations array: fields AggregationLevelConfiguration.CodeType and AggregationLevelConfiguration.HelperCodeType.
I think I have to set selected value in the View, but I don't know how to do it.
How can I set the selected values?
Unfortunately #Html.DropDownListFor() behaves a little differently than other helpers when rendering controls in a loop. This has been previously reported as an issue on CodePlex (not sure if its a bug or just a limitation)
The are 2 option to solve this to ensure the correct option is selected based on the model property
Option 1 (using an EditorTemplate)
Create a custom EditorTemplate for the type in the collection. Create a partial in /Views/Shared/EditorTemplates/AggregationLevelConfiguration.cshtml (note the name must match the name of the type
#model yourAssembly.AggregationLevelConfiguration
#Html.DropDownListFor(m => m.HelperCodeType, (SelectList)ViewData["CodeTypeItems"])
.... // other properties of AggregationLevelConfiguration
and then in the main view, pass the SelectList to the EditorTemplate as additionalViewData
#using (Html.BeginForm())
{
...
#Html.EditorFor(m => m.Configurations , new { CodeTypeItems = Model.CodeTypeItems })
...
Option 2 (generate a new SelectList in each iteration and set the selectedValue)
In this option your property CodeTypeItems should to be IEnumerable<GenericIdNameType>, not a SelectList (or just make codeTypes a public property). Then in the main view
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, new SelectList(Model.CodeTypeItems, "Id", "Name", Model.Configurations[0].HelperCodeType)
Side note: there is no need to use new { id = "Configurations[0].HelperCodeType" - the DropDownListFor() method already generated that id attribute
I wrote this class to overcome an issue I was having with selecting an option in an html select list. I hope it helps someone.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace Login_page.Models
{
public class HTMLSelect
{
public string id { get; set; }
public IEnumerable<string> #class { get; set; }
public string name { get; set; }
public Boolean required { get; set; }
public string size { get; set; }
public IEnumerable<SelectOption> SelectOptions { get; set; }
public HTMLSelect(IEnumerable<SelectOption> options)
{
}
public HTMLSelect(string id, string name)
{
this.id = id;
this.name = name;
}
public HTMLSelect(string id, string name, bool required, IEnumerable<SelectOption> options)
{
this.id = id;
this.name = name;
this.required = required;
}
private string BuildOpeningTag()
{
StringBuilder text = new StringBuilder();
text.Append("<select");
text.Append(this.id != null ? " id=" + '"' + this.id + '"' : "");
text.Append(this.name != null ? " name=" + '"' + this.name + '"' : "");
text.Append(">");
return text.ToString();
}
public string GenerateSelect(IEnumerable<SelectOption> options)
{
StringBuilder selectElement = new StringBuilder();
selectElement.Append(this.BuildOpeningTag());
foreach (SelectOption option in options)
{
StringBuilder text = new StringBuilder();
text.Append("\t");
text.Append("<option value=" + '"' + option.Value + '"');
text.Append(option.Selected != false ? " selected=" + '"' + "selected" + '"' + ">" : ">");
text.Append(option.Text);
text.Append("</option>");
selectElement.Append(text.ToString());
}
selectElement.Append("</select");
return selectElement.ToString();
}
}
public class SelectOption
{
public string Text { get; set; }
public Boolean Selected { get; set; }
public string Value { get; set; }
}
}
And
public IEnumerable<SelectOption> getOrderTypes()
{
List<SelectOption> orderTypes = new List<SelectOption>();
if (this.orderType == "OptionText")
{
orderTypes.Add(new SelectOption() { Value = "1", Text = "OptionText", Selected = true });
} else
{
orderTypes.Add(new SelectOption() { Value = "2", Text = "OptionText2" });
}
}
And to use it:
#{
Login_page.Models.HTMLSelect selectElement = new Login_page.Models.HTMLSelect("order-types", "order-types");
}
#Html.Raw(selectElement.GenerateSelect(Model.getOrderTypes()));
I leave this in case it helps someone else. I had a very similar problem and none of the answers helped.
We had in a view this line at the top:
IEnumerable<SelectListItem> exitFromTrustDeed = (ViewData["ExitFromTrustDeed"] as IEnumerable<string>).Select(e => new SelectListItem() {
Value = e,
Text = e,
Selected = Model.ExitFromTrustDeed == e
});
and then below in the view:
#Html.DropDownListFor(m => m.ExitFromTrustDeed, exitFromTrustDeed, new { #class = "form-control" })
We had a property in my ViewData with the same name as the selector for the lambda expression and for some reason that makes the dropdown to be rendered without any option selected.
We changed the name in ViewData to ViewData["ExitFromTrustDeed2"] and that made it work as expected.
Weird though.

A circular reference was detected while serializing entities with one to many relationship

How to solve one to many relational issue in asp.net?
I have Topic which contain many playlists.
My code:
public class Topic
{
public int Id { get; set; }
public String Name { get; set; }
public String Image { get; set; }
---> public virtual List<Playlist> Playlist { get; set; }
}
and
public class Playlist
{
public int Id { get; set; }
public String Title { get; set; }
public int TopicId { get; set; }
---> public virtual Topic Topic { get; set; }
}
My controller function
[Route("data/binding/search")]
public JsonResult Search()
{
var search = Request["term"];
var result= from m in _context.Topics where m.Name.Contains(search) select m;
return Json(result, JsonRequestBehavior.AllowGet);
}
When I debug my code I will see an infinite data because Topics will call playlist then playlist will call Topics , again the last called Topic will recall playlist and etc ... !
In general when I just use this relation to print my data in view I got no error and ASP.NET MVC 5 handle the problem .
The problem happens when I tried to print the data as Json I got
Is there any way to prevent an infinite data loop in JSON? I only need the first time of data without call of reference again and again
You are getting the error because your entity classes has circular property references.
To resolve the issue, you should do a projection in your LINQ query to get only the data needed (Topic entity data).
Here is how you project it to an anonymous object with Id, Name and Image properties.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you have a view model to represent the Topic entity data, you can use that in the projection part instead of the anonymous object
public class TopicVm
{
public int Id { set;get;}
public string Name { set;get;}
public string Image { set;get;}
}
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new TopicVm
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you want to include the Playlist property data as well, you can do that in your projection part.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image,
Playlist = x.Playlist
.Select(p=>new
{
Id = p.Id,
Title = p.Title
})
});
return Json(result, JsonRequestBehavior.AllowGet);
}

Radio Button For multiple bools

Say I have the following properties in my model that I want to be mutually exclusive:
public bool PrintWeek1 {get; set;}
public bool PrintWeek2 {get; set;}
public bool PrintWeek3 {get; set;}
Is it possible to render these as a set of radio buttons or do I need to change them to an enum?
If I use #Html.RadioButtonFor it renders name as the name of the property so they aren't grouped correctly.
Here comes a quick solution, let you have following properties in Model -
public bool PrintWeek1 { get; set; }
public bool PrintWeek2 { get; set; }
public bool PrintWeek3 { get; set; }
public string SelectedValue { get; set; }
Then your HTML should be like this -
#Html.RadioButtonFor(Model => Model.PrintWeek1, "PrintWeek1", new { #Name = "SelectedValue" })
#Html.RadioButtonFor(Model => Model.PrintWeek2, "PrintWeek2", new { #Name = "SelectedValue" })
#Html.RadioButtonFor(Model => Model.PrintWeek3, "PrintWeek3", new { #Name = "SelectedValue" })
Then when you submit the form, you will get the selected value in SelectedValue property.
EDIT
To Address #StephenMuecke point, created the below solution -
Create a enum -
public enum PrintWeekType
{
PrintWeek1, PrintWeek2, PrintWeek3
}
Then have a model property (instead of individual properties, have single emum property) -
public PrintWeekType SelectedValue { get; set; }
HTML should be like below -
#Html.RadioButtonFor(m => m.SelectedValue, PrintWeekType.PrintWeek1)
#Html.RadioButtonFor(m => m.SelectedValue, PrintWeekType.PrintWeek2)
#Html.RadioButtonFor(m => m.SelectedValue, PrintWeekType.PrintWeek3)
Using above sample, one can pre-select a radiobutton, at the same time we can post the selected value in SelectedValue property.
Ok I abandoned the bools and just ended up using a list - this seemed to be the quickest and easiest way to do it.
Where I initialize my model:
public PrintViewModel()
{
this.PrintTypes = new List<string>() { "Print Week 1", "Print Week 2", "Print Week 3" };
}
public List<string> PrintTypes { get; set; }
public string SelectedPrintType { get; set; }
In my view (I wanted the first option selected by default):
#for(int i = 0; i < Model.PrintTypes.Count; i++)
{
<div class="row">
<div class="col-md-2">
#(i == 0 ? Html.RadioButtonFor(x => x.SelectedPrintType, Model.PrintTypes[i], new {#checked = "checked"}) : #Html.RadioButtonFor(x => x.SelectedPrintType, Model.PrintTypes[i]))
<label for="#Model.PrintTypes[i]">#Model.PrintTypes[i]</label>
</div>
</div>
}

get IEnumerable<T> in mvc post method argument

I have one model called ProductSupplier
I am passing #model IEnumerable to my View
and showing it from view
Now when i submit the form i m not getting list of IEnumerable in my http post method. I want to know the selected supplier from user.
Below is my model
public sealed class ProductSupplier
{
public int CountryId { get; set; }
public int UserId { get; set; }
public bool IsProductSupplier { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
This is my HttpGet method
public ActionResult ManageSupplier(int id)
{
var supplier = App.UsersRepo.GetSupplierForProduct(id);
return View(supplier);
}
And I am binding it via following way (U can suggest me best way I am new bee to MVC)
#model IEnumerable<ProductSupplier>
#using (Html.BeginForm("ManageSupplier", "Products", FormMethod.Post, new { role = "form") })
{ #Html.AntiForgeryToken()
foreach (var item in Model)
{
<div class="checkbox">
<label>
#Html.CheckBoxFor(x => item.IsProductSupplier, new { id = item.Email }) #item.FirstName #item.LastName (#item.Email)
</label>
</div>
}
}
And finally my HttpPost method
[HttpPost]
public ActionResult ManageSupplier(IEnumerable<ProductSupplier> obj)
{ // I m getting obj null in my argument
//I want to Get selected id from obj and want to pass in selectedSupplier
var returnVal = App.ProductRepo.AssigneSupplierForProduct(productId, selectedSupplier);
return Json(new { success = true }, JsonRequestBehavior.DenyGet);
}
can anyone suggest me where i m making mistake.
I am new to MVC any kind of suggestion highly appreciated.
Thank you in advance.
Firstable u cant do it like this.One way to do that is something like this.Here is the basic step how u do that.
1-assign for all checkbox ,checkbox change event with the unique id.
(take a look at here)
2-Cretae a jquery object and store the data when ever the checkbox clicked ,via versa
var ListProductSuppliers ={ {ProductSupplier_info_here },{ProductSupplier_info_here } };
3-later via ajax request,serilize this object(ListProductSuppliers ) and send to your method
4-on server side deserilize this to the IEnumerable<ProductSupplier>
5 later do it whatever u want with those selected suppliars

Resources