My problem is very similar to this Model binding issues with Kendo objects with complex child properties . The only difference is that i have another level in the object.
My model is:
Public Person
{
public int Id {get;set;}
public string Name {get;set;}
public IEnumerable<Course> Courses {get;set;}
}
public Course
{
public int Id {get;set;}
public string Description {get;set;}
public IEnumerable<Schedule> Schedules {get;set;}
}
Public Schedule
{
public DateTime Init {get;set;}
public DateTime End {get;set;}
}
This model is bound to a KendoGrid. Everything works well, except that Init and End properties are always null when I posted the model.
In the Ajax Datasource :
.Update(update => update.Action("Update", "Controller").Data("serialize"))
.Create(create => create.Action("Create", "Controller").Data("serialize"))
<script>
function serialize(data) {
for (var property in data) {
if ($.isArray(data[property])) {
serializeArray(property, data[property], data);
}
}
};
function serializeArray(prefix, array, result) {
for (var i = 0; i < array.length; i++) {
if ($.isPlainObject(array[i])) {
for (var property in array[i]) {
result[prefix + "[" + i + "]." + property] = array[i][property];
}
}
else {
result[prefix + "[" + i + "]"] = array[i];
}
}
}
</script>
What I have to do to send the properties of the lists schedules?
I had also looked at their serializeArray solution, but it didn't work for me in case of 3 level objects I had. I could have fixed that but then I didn't want to write recursive code. The solution I used is pretty straight-forward and aligned to the problem I had. Its very readable.
I absolutely wish Kendo should do this out of the box for their grid, but they told this when I raised a support question.
"You will need to send the values as additional data in this case because the built-in filtering does not support collection values. To format the data so that it will be bound by the model binder, you should follow the guidelines from my previous reply(dot notation for objects and indexer for arrays)"
Here is my C# ViewModels
//relates to one control value (for e.g. one entry in multi-select)
public class FormUnitFilter
{
public string Operator { get; set; }
public string Field { get; set; }
public string Value { get; set; }
public List<string> ValueList { get; set; }
}
//relates to a set of filters in a combined set (for e.g. the whole multi-select or a radiobutton or date control which appears in a single panel)
public class FormSetFilter
{
public List<FormUnitFilter> Filters { get; set; }
public string LogicalOperator { get; set; }
}
//relates to the whole set of filters present on the screen (for e.g. the filters across different panels)
public class FormWholeFilter
{
public List<FormSetFilter> Filters { get; set; }
public string LogicalOperator { get; set; }
}
here is my js function which converts this json model to a type recognized by MVC controller action parameter.
function buildFilterCriteria() {
var data = {};
if (modelObj) {
//reset the filters
modelObj.FormWholeFilter.Filters.length = 0;
//Assign FormWholeFilter data (outermost object)
data["FormWholeFilter.LogicalOperator"] = modelObj.FormWholeFilter.LogicalOperator;
//now iterate the filters inside FormWholeFilter (1st inner object)
for (var setIndex = 0; setIndex < modelObj.FormWholeFilter.Filters.length; setIndex++) {
var setFilter = modelObj.FormWholeFilter.Filters[setIndex];
data["FormWholeFilter.Filters[" + setIndex + "].LogicalOperator"] = setFilter.LogicalOperator;
//now iterate the filters inside FormSetFilter (2nd inner object)
for (var unitIndex = 0; unitIndex < setFilter.Filters.length; unitIndex++) {
var unitFilter = setFilter.Filters[unitIndex];
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Operator"] = unitFilter.Operator;
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Field"] = unitFilter.Field;
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Value"] = unitFilter.Value;
if (unitFilter.ValueList)
for (var valIndex = 0; valIndex < unitFilter.ValueList.length; valIndex++) {
data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].ValueList[" + valIndex + "]"] = unitFilter.ValueList[valIndex];
}
}
}
}
return modelObj && data;
}
Here is my controller action method which takes the Kendo grid datasourcerequest and the FormWholeFilter I pass from JavaScript.
public JsonResult ProcessFilters([DataSourceRequest] DataSourceRequest request, FormWholeFilter formWholeFilter)
{
//Method body
}
Also, when I load the page for the first time, I had assigned the modelObj to the FormWholeFilter blank json like this and thats why I could use this variable in the buildFilterCriteria method:
var modelObj;
$(document).ready(function () {
modelObj = $.parseJSON('#Html.Raw(Json.Encode(#Model))');
});
Related
I wanted to get from a database a IEnumerable<T> and within the 'Select' method using a function to return a string value. But I always get back the
'method cannot be translated into a store expression'
error.
I already took a look all the post on Stack Overflow about the error 'LINQ to Entities does not recognize the method .... and this method cannot be translated into a store expression"
The only way that I found to get around this error is apply the function after the query has run.
void Main()
{
int eventId = 17;
IEnumerable<OccurrenceDropDownList> model = Occurrences.Select (s => new OccurrenceDropDownList
{
OccurrenceId = s.OccurrenceId,
Name = s.Name
})
.AsEnumerable()
.Select(m => new OccurrenceDropDownList
{
OccurrenceId = m.OccurrenceId,
Name = m.Name,
Selected = setSelected(m.OccurrenceId, eventId)
}).AsEnumerable();
foreach(var item in model)
{
Console.WriteLine(item.Name + " - id : " + item.OccurrenceId + " " + item.Selected);
}
}
public class OccurrenceDropDownList
{
public int OccurrenceId { get; set; }
public string Name { get; set; }
public string Selected { get; set; }
}
static string setSelected(int occurrence, int selectedid){
if(occurrence == selectedid){
return "selected";
}
return "";
}
Is there any way to apply the function as result of the first query?
It should be simplier:
int eventId = 17;
IEnumerable<OccurrenceDropDownList> model = Occurrences
.Select(s => new OccurrenceDropDownList
{
OccurrenceId = s.OccurrenceId,
Name = s.Name,
//magic ternary if
Selected = (eventId == s.OccurrenceId) ? "selected" : String.Empty
});
That's all. I used ternary if operator that should be translated to SQL.
I have a Kendo UI grid which I need to be editable. I also need a client template for one of the columns.
The part from the kendo ui grid that bugs me:
.Columns(columns =>
{
columns.Bound(p => p.Author).Filterable(false).Width(100);
columns.Bound(p => p.Name).Filterable(false).Title("Idea Name");
columns.Bound(p => p.Description).Filterable(false);
columns.Bound(p => p.BeginDate).Format("{0:d}");
columns.Bound(p => p.ReleaseDate).Format("{0:d}");
columns.Bound(p => p.NextAvailableStates).ClientTemplate
(
"#for(var i = 0; i < NextAvailableStates.length; i++) {" +
"# <li>" +
" #=NextAvailableStates[i].StepName # " +
"</li> #" +
"} #"
)
.Title("Actions").IncludeInMenu(false).Visible(true)
.Sortable(false).Filterable(false);
})
My controller:
public partial class IdeaController : Controller
{
private StateMachineHelper helper = new StateMachineHelper();
public ActionResult Index(int? state)
{ //must ad to Model the next available states
var model = new List<Idea>();
model.AddRange(helper.GetIdeasByState(helper.GetStateByID(state)));
foreach (var item in model)
{
item.NextAvailableStates = helper.GetNextStates(helper.GetStateMachineInstancesByOrderID(item.ID));
}
return View(model);
}
public ActionResult NextState()
{
return View();
}
public ActionResult Read([DataSourceRequest]DataSourceRequest request, DateTime start, DateTime end)
{
var ideas = helper.GetIdeasBetweenDates(start, end);
DataSourceResult result = ideas.ToDataSourceResult(request);
return Json(result);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([DataSourceRequest] DataSourceRequest request, Idea product)
{
if (product != null && ModelState.IsValid)
{
var helper = new StateMachineHelper();
var machine = new StateMachineInstance() {
ParentID = 0,
//1 -> machineID
CurrentStateID = helper.GetFirstStateOfMachine(1).ID,
MachineID =1,
ParentStateID =0,
//OrderType: idea/command
OrderType = "Idea"
};
helper.AddNewOrder(product, machine);
product.NextAvailableStates = helper.GetNextStates(machine);
}
return Json(new[] { product }.ToDataSourceResult(request, ModelState));
}
}
public Idea()
{
this.StateMachineInstances = new HashSet<StateMachineInstance>();
}
[ScaffoldColumn(false)]
[DataMember]
public int ID { get; set; }
[Required]
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
[Required]
public string Author { get; set; }
[DataMember]
public System.DateTime BeginDate { get; set; }
[DataMember]
public Nullable<System.DateTime> ReleaseDate { get; set; }
[DataMember]
public List<State> NextAvailableStates
{
get
{
return nextAvailableStates;
}
set
{
nextAvailableStates = value;
}
}
private List<State> nextAvailableStates = new List<State>();
public virtual ICollection<StateMachineInstance> StateMachineInstances { get; set; }
}
The grid renders just fine, with every column filled with the correct information. The problem appears when I need to add a new item to the grid. The popup window doesn't show up and I get Uncaught ReferenceError: NextAvailableStates is not defined. Which actually makes sense because the grid tries to draw before the new item gets returned.
My question: is there a way to achieve the insertion of a new item while using custom client templates?
You can try modifying your client template to check for the existence of NextAvailableStates before using them:
"# if (NextAvailableStates) {" +
" for(var i = 0; i < NextAvailableStates.length; i++) {" +
"# <li>" +
" #=NextAvailableStates[i].StepName # " +
"</li> #" +
" } " +
"} #"
i need to use ViewBag as list inside another ViewBag and i am not understanding how should i do that.
here is my code:
List<string> srclist = new List<string>();
foreach(var item in candidateportfolio)
{
if(item.PortfolioID!=0 && item.ChandidateID!=0)
{
string filepath = Server.MapPath("~/ePortfolio/PortFolioContent/" + HobbyDetailID + "/Assignments/Exhb_" + item.PortfolioID + "_" + item.ChandidateID + ".jpg");
if (System.IO.File.Exists(filepath))
{
srclist.Add(filepath);
}
}
}
ViewBag.Thumbnail = srclist;
candidateportfolio is an object of the class CandidatePortfolio.I fetch the data in the class and check whether its fields are not empty.Then i add the filepath to the list and assign list to Viewbag
Then in View i use it like this:
#foreach (var item in ViewBag.Thumbnail as List<string>)
{
<img src="#item" title="Learner:#ViewBag.FirstName" width="150px" height="150px" border="5" style="align:right;margin:10px"/>
}
Now the problem is i also want to fetch ViewBag.FirstName as list.and i cannot run another list in this.Please tell me how should i do this.
If you want a list containing both FirstName and the path to, say, a photo you can create a new class:
public class ThumbnailModel
{
public string FirstName { get; set; }
public string PhotoPath { get; set; }
}
Now you can add a List<Thumbnail> to the ViewBag.
But I would suggest just creating a strongly typed view with a relvant model containing a List<Thumbnail> property.
user1274646,
your best bet here is to create a public class that contains both the exisiting thumbnail element plus an additional FirstName property. Here's how this might look:
public class CandidateItem{
public string FirstName {get; set;}
public string Filepath {get; set;}
}
then, in your loop, create a new CandidateItem and add it to the list (i.e. List). Here's the amended code:
List<CandidateItem> srclist = new List<CandidateItem>();
foreach(var item in candidateportfolio)
{
if(item.PortfolioID!=0 && item.ChandidateID!=0)
{
CandidateItem candItem = new CandidateItem();
candItem.Filepath = Server.MapPath("~/ePortfolio/PortFolioContent/" + HobbyDetailID + "/Assignments/Exhb_" + item.PortfolioID + "_" + item.ChandidateID + ".jpg");
candItem.FirstName = item.FirstName;
if (System.IO.File.Exists(filepath))
{
srclist.Add(candItem);
}
}
}
ViewBag.Thumbnail = srclist;
then use it in the view as:
#foreach (var item in ViewBag.Thumbnail as List<CandidateItem>)
{
<img src="#item.Filepath" title="Learner:#item.FirstName" width="150px" height="150px" border="5" style="align:right;margin:10px"/>
}
I'm trying to adapt the answers for filling a ListBoxFor that has preselected values that I've found here on SO and am having some trouble with the adaptation.
This is one of the questions I've been referring to: here
Here is my class that defines a list object
public class SelectListDTO {
public int ID { get; set; }
public string Name { get; set; }
}
I have a method on a class that fills a List of SelectListDTO items. The CheckRefresh checks to see if the cache is expired, if so it refills the cache. So this method gets my list:
private List<SelectListDTO> GetSelectList() {
CheckRefresh();
var lst = new List<SelectListDTO>(_cache.Count + 1);
_cache.ForEach(item => lst.Add(new SelectListDTO { ID = item.ID, Name = item.Name }));
return lst;
}
My Model is define with these Properties/Methods:
public class MyModel {
[Required]
[Display(Name = "Program Type")]
[Min(1, ErrorMessage = "Please select a Program Type")]
public int[] SelectedProgramTypes { get; set; }
public MultiSelectList ProgramTypes { get; set; }
public MyModel() {
PopulateProgramTypeList();
}
private void PopulateProgramTypeList() {
// Get all available list items
var programTypes = ProgramTypeService.Instance.GetSelectList;
// how to fill multiselectlist with my List<SelectListDTO> items;
ProgramTypes = new MultiSelectList(??)
}
}
1st part of question is from above here^ How to fill the MultiSlectList with my List of SelectListDTO objects
Also in my controller action I am getting the saved items from the DB and will need to pass them to the model as SelectedProgramTypes. This is currently in my action:
public ActionResult Edit(int? id) {
// Code here to validate id and that user is associated with id
lenderProduct = new LenderProduct(id);
var model = BuildModel(lenderProduct); // returns instance or MyModel
var selectedProgramTypes = lenderProduct.ProgramTypes;
foreach (var item in selectedProgramTypes) {
/// How to fill the Model.SelectedProgramTypes array
}
return View(model);
}
2nd part of question is how to get the currently selected items that I read from the DB into the array that can be used by the MultiSelectList in the Model
I feel like I'm this close but am missing some pattern or hopefully just the correct syntax into getting this to work in this way as opposed to the ways I've seen posted here.
I haven't made it to the View yet but from what I've seen that is just as easy as filling a normal DropDownList.
1st part of question is from above here^ How to fill the
MultiSlectList with my List of SelectListDTO objects
ProgramTypes = new MultiSelectList(programTypes.Select(x => new SelectListItem
{
Value = x.ID.ToString(),
Text = x.Name
}));
2nd part of question is how to get the currently selected items that I
read from the DB into the array that can be used by the
MultiSelectList in the Model
It's not clear how your LenderProduct class looks like but assuming the ProgramTypes property is just an array of integers you could directly assign it to your view model:
public ActionResult Edit(int? id)
{
// Code here to validate id and that user is associated with id
var lenderProduct = new LenderProduct(id);
var model = BuildModel(lenderProduct); // returns instance or MyModel
model.SelectedProgramTypes = lenderProduct.ProgramTypes;
return View(model);
}
and if it is an array of some complex object you could select the corresponding property that contains the id:
public ActionResult Edit(int? id)
{
// Code here to validate id and that user is associated with id
var lenderProduct = new LenderProduct(id);
var model = BuildModel(lenderProduct); // returns instance or MyModel
model.SelectedProgramTypes = lenderProduct.ProgramTypes.Select(x => x.ID).ToArray();
return View(model);
}
I've got a question regarding the Telerik RadGrid control client side binding. I want to populate grid with the cities on the Client Side. I've got a object City, which has a property Country:
[DataContract]
[KnownType(typeof(Country))]
public class City
{
[DataMember]
public virtual string CityCode { get; set; }
[DataMember]
public virtual string CityName { get; set; }
[DataMember]}
public virtual Country Country { get; set;
}
}
[DataContract]
public class Country
{
[DataMember]
public virtual string CountryCode { get; set; }
[DataMember]
public virtual string Iso2Code { get; set; }
[DataMember]
public virtual string CountryName { get; set; }
[DataMember]
public virtual char RDC { get; set; }
}
I retrieve this data as a JSON object to the client side using the JQuery Ajax and WCF.
and then I bind it to the grid:
rgCity.set_dataSource(dataItem);
rgCity.dataBind();
Here are the Columns definition for the grid:
<Columns>
<telerik:GridBoundColumn HeaderText="City Code" DataField="CityCode" MaxLength="3"> </telerik:GridBoundColumn>
<telerik:GridBoundColumn HeaderText="City Name" DataField="CityName"></telerik:GridBoundColumn>
<telerik:GridBoundColumn HeaderText="Country Code" DataField="CountryCode" MaxLength="2"></telerik:GridBoundColumn>
</Columns>
The problem is I'm not getting the Country Code column populated with the data. I think the problem is in data binding, but I'm not sure if is it possible to bind a complex objects.
I think should be something like that:
<telerik:GridBoundColumn HeaderText="Country Code" DataField="**City.CountryCode**" MaxLength="2"></telerik:GridBoundColumn>
I appreciate any help solving that issue!
You can just overwrite the
Telerik.Web.UI.GridTableView.dataBind function by replacing
this snippet from the original minified telerik script:
var J = r[v].get_uniqueName();
var n = this.getCellByColumnUniqueName(H, J);
if (!n) {
continue;
}var D = r[v]._data.DataField;
if (typeof (D) == "undefined") {
D = J;
} var h = this._dataSource[u][D];
if (h == null) {
h = "";
with something, for example, like this:
var J = r[v].get_uniqueName();
var n = this.getCellByColumnUniqueName(H, J);
if (!n) {
continue;
}var D = r[v]._data.DataField;
if (typeof (D) == "undefined") {
D = J;
}
//change here to eval the dataField
var h = AvoidEvalDueToPerformance(this._dataSource[u], D);
if (h == null) {
h = "";
AvoidEvalDueToPerformance function is defined as follows:
function AvoidEvalDueToPerformance(object, propertyString) {
var k = propertyString.split(".");
for (var i = 0; i < k.length; i++)
if (object[k[i]]) object = object[k[i]];
else return object;
return object;
}
Hope this helps someone as this was the first result i stumbled upon searching for the answer of question "How to bind RadGrid to complex object clientside"
P.S. to overwrite a function you could write
Telerik.Web.UI.GridTableView.dataBind.prototype = function(){
//copy-paste the Telerik.Web.UI.GridTableView.dataBind.prototype contents
//from your favorite javascript debugger output
//(or grep output or w/e you prefer) here :)<br />
}
I don't think you can do complex databinding like that. Instead, I'd make a new property that returned the Country Code directly, then bind to that. Example:
[DataContract]
[KnownType(typeof(Country))]
public class City
{
...
[DataMember]}
public virtual CountryCode{ get Country.CountryCode; }
}
Declarative databinding for the grid then is what you had:
<Columns>
...
<telerik:GridBoundColumn HeaderText="Country Code" DataField="CountryCode" MaxLength="2"></telerik:GridBoundColumn>