Telerik MVC Grid with Dynamic Columns at Run Time from a Collection or Dictionary - asp.net-mvc-3

After spending the last couple days searching, I'm officially stuck. I'm working on a binding an object to the Telerik MVC 3 Grid, but the catch is that it needs to have dynamically created columns (not auto generated). Three of the columns are known, the others are unknown, and this is the tricky part. Basically, it can be like these examples:
KnownColumn1 | KnownColumn2 | UnknownColumn1 | KnownColumn3
KnownColumn1 | KnownColumn2 | UnknownColumn1 | UnknownColumn2 | UnknownColumn3 | KnownColumn3
etc.
Because I'm putting the unknown columns in a list (I've tried a dictionary too so I can get the column names), this has complicated things for me when binding. My code is below:
Model (There can be zero to hundreds of rows, but this model is in a view model of type List, there can also be 0 to 20 plus columns that are dynamically added)
public class VendorPaymentsGLAccount
{
public string GeneralLedgerAccountNumber { get; set; }
public string GeneralLedgerAccountName { get; set; }
public string DisplayName { get { return string.Format("{0} - {1}", GeneralLedgerAccountNumber, GeneralLedgerAccountName); } }
public Dictionary<string, double> MonthAmount { get; set; }
public double Total { get { return MonthAmount.Sum(x => x.Value); } }
public List<string> Columns { get; set; }
public List<double> Amounts { get; set; }
public VendorPaymentsGLAccount() { }
}
View (The section that's commented out was trying to use the dictionary)
<fieldset>
<legend>General Ledger Account Spend History</legend>
#if (Model.VendorPaymentsGLAccounts != null)
{
<br />
#(Html.Telerik().Grid(Model.VendorPaymentsGLAccounts)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(gl => gl.DisplayName).Title("General Ledger Account").Width(200).Filterable(false).Sortable(false);
//foreach (var month in Model.VendorPaymentsGLAccounts[0].MonthAmount)
//{
// //columns.Bound(gl => gl.MonthAmount[month.Key.ToString()].ToString()).Title(month.Key.ToString()).Width(100).Filterable(false).Sortable(false);
// //columns.Template(v => Html.ActionLink(v.VoucherID, "VoucherSummary", new { id = v.VoucherID, bu = v.BusinessUnitID, dtt = v.InvoiceDate.Ticks })).Title("Voucher").Width(100);
// columns.Template(gl => Html.ActionLink(gl.MonthAmount[month.Key.ToString()].ToString(), "VoucherSummary")).Title(month.Key.ToString()).Width(100);
//}
for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
{
string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
columns.Template(gl => gl.Amounts[i - 1]).Title(colTemp).Width(100);
}
columns.Template(gl => String.Format("{0:C}", gl.Total)).Title("Total");
})
.Sortable()
.Pageable()
.Filterable()
.Footer(true))
}
else
{
<br />
#:There are no records that match your selected criteria.
}
</fieldset>
Using the dictionary approach, I was able to get the columns generated correctly with the right header text, but the values for the columns (in my testing there were only 2 columns) were the same. Can anyone help with this? This seems to be an oddball issue. Just trying to figure out how to do this correctly.
Update: Here is a screen shot using the dictionary approach that shows the issue. The column headings are correct, but the values are the same for both of the dynamic columns.

Using dynamically defined columns with the Telerik grid control can be tricky. But in your case, it's mainly a typical pitfall of closures.
In the following loop, the compiler will bind each instance of gl => gl.Amounts[i - 1] to the variable i and evaluate it later:
for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
{
string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
columns.Template(gl => gl.Amounts[i - 1]).Title(colTemp).Width(100);
}
In fact, it's evaluated after the loop has finished. So i will always have the value that lead to the completion of the loop.
The fix is to use a temporary variable:
for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
{
string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
int columnIndex = i - 1;
columns.Template(gl => gl.Amounts[columnIndex]).Title(colTemp).Width(100);
}

I had the same problem, and was googling plenty of hours around, and have done a lot of attempts from various assistance lines. But even so, it was not so trivial to solve!
And for that reason and to have also another working example here, I will provide also my solution!
Information: It only works at my place with an IList Model. Other collections had also caused problems!
#model IList<CBS.Web.Models.Equipment.EquipmentViewModel>
#(Html.Telerik().Grid(Model)
.Name("Grid")
.DataKeys(keys =>
{
keys.Add(m => m.ID);
})
.DataBinding(dataBinding =>
{
dataBinding.Ajax()
// renders the grid initially
.Select("EquipmentGrid", "Equipment");
})
.Columns(columns =>
{
// Equipment IDs
columns.Bound(m => m.ID).Hidden(true);
columns.Bound(m => m.Name).Title("Equipments").Width(200);
// Every item (EquipmentViewModel) of the Model has the same Count of Fields
for (int i = 0; i < (Model[0].Fields.Count()); i++)
{
// Name of the column is everytime same as in Model[0]
string columnName = Model[0].Fields.ElementAt(i).FieldDefinition.Name;
// Constructs i-counted columns, dynamically on how much
// Fields are owned by an Equipment. But note, that all Equipment-items
// in the Model must have the same Count and disposal of Fields!
columns.Template(m => m.Fields
.Where(f => f.FieldDefinition.Name == columnName)
.Where(f => f.EquipmentId == m.ID).First().Value)
.Title(columnName)
.Width(columnName.Length * 8); // * 8 was the optimal lenght per character
}
})
.ClientEvents(events => events.OnRowSelect("onRowSelected"))
.Selectable()
.Resizable(resizing => resizing.Columns(true))
.Pageable()
.Scrollable()
.Groupable()
.Filterable()
)
Controller:
public ActionResult EquipmentGrid(Guid id)
{
var belongingEquipments = _equipmentRepository.GetNotDeleted()
.OrderBy(e => e.Name).ToList()
.Where(e => e.RevisionId == id);
List<EquipmentViewModel> equVMList = new List<EquipmentViewModel>();
for (int i = 0; i < belongingEquipments.Count(); i++)
{
var equVM = new EquipmentViewModel
{
ID = belongingEquipments.ElementAt(i).ID,
Name = belongingEquipments.ElementAt(i).Name,
RevisionId = belongingEquipments.ElementAt(i).RevisionId,
EquipmentTypeId = belongingEquipments.ElementAt(i).EquipmentTypeId,
Fields = SortFields(belongingEquipments.ElementAt(i).Fields.ToList())
};
equVMList.Add(equVM);
}
return PartialView("EquipmentGrid", equVMList);
}
Models:
namespace CBS.Web.Models.Equipment
{
public class EquipmentViewModel
{
public Guid ID { get; set; }
public string Name { get; set; }
public Guid RevisionId { get; set; }
public Guid EquipmentTypeId { get; set; }
public virtual ICollection<FieldEntity> Fields { get; set; }
}
}
FieldDefinition
namespace CBS.DataAccess.Entities
{
public class FieldDefinitionEntity : EntityBase
{
[Required]
public virtual Guid EquipmentTypeId { get; set; }
public virtual EquipmentTypeEntity EquipmentType { get; set; }
[Required(AllowEmptyStrings = false)]
public virtual string Name { get; set; }
public virtual int Numbering { get; set; }
[Required]
public virtual Guid TypeInformationId { get; set; }
public virtual TypeInformationEntity TypeInformation { get; set; }
public virtual ICollection<FieldEntity> Fields { get; set; }
}
}
Field
namespace CBS.DataAccess.Entities
{
public class FieldEntity : EntityBase
{
[Required]
public virtual Guid EquipmentId { get; set; }
public virtual EquipmentEntity Equipment { get; set; }
[Required]
public virtual Guid FieldDefinitionId { get; set; }
public virtual FieldDefinitionEntity FieldDefinition { get; set; }
public virtual string Value { get; set; }
}
}

I dynamically bind the columns at runtime with reflection:
#model IEnumerable<object>
#using System.Collections
#using System.Collections.Generic
#using System.Reflection;
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
Type t = Model.GetType().GetGenericArguments()[0];
foreach (var prop in t.GetProperties())
{
if (IsCoreType(prop.PropertyType))
{
columns.Bound(prop.PropertyType, prop.Name);
}
}
})
.DataBinding(binding => binding.Ajax()
.Select("SelectMethod", "SomeController")
)
.Sortable()
.Pageable()
.Filterable()
.Groupable()
)
#functions{
public bool IsCoreType(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return IsCoreType(type.GetGenericArguments()[0]);
}
return !(type != typeof(object) && Type.GetTypeCode(type) == TypeCode.Object);
}
}

Here is the workaround:
#(Html.Telerik().Grid(Model.Users)
.Name("Grid")
.Columns(columns => {
columns.GenerateCustomColumns(columnSettings);
}).DataBinding(dataBinding => dataBinding.Ajax().Select("_getusers", "home"))
.Scrollable(scrolling => scrolling.Enabled(true).Height("auto"))
.Pageable(paging => paging.Enabled(true)
.PageSize(10, new int[] { 5, 10, 20, 50, 100, 500 })
.Position(GridPagerPosition.Both)
.Total(Model.Users.Count)
.Style(GridPagerStyles.PageSizeDropDown | GridPagerStyles.NextPreviousAndNumeric)
.PageTo(1))
.Filterable(filtering => filtering.Enabled(true))
.Reorderable(reordering => reordering.Columns(true))
.NoRecordsTemplate(" ")
.EnableCustomBinding(true)
)
// Extension method to genarate columns dynamically
public static class TelerikMvcGridColumnHelper
{
public static void GenerateCustomColumns<T>(this GridColumnFactory<T> columns,List<GridCustomColumnSettings> settings) where T:class
{
if (settings != null)
{
settings.ForEach(column =>
{
var boundedColumn = columns.Bound(column.Member);
if (column.ClientFooterTemplate != null)
boundedColumn.ClientFooterTemplate(column.ClientFooterTemplate);
if (!string.IsNullOrEmpty(column.Width))
boundedColumn.Width(column.Width);
});
}
}
}
// Column settings class
public class GridCustomColumnSettings : GridColumnSettings
{
public string ClientFooterTemplate { get; set; }
}

I did this simple way. NOTE : the following solution works in ajax edit mode too (not just a read-only grid) :
when the ViewModels are :
public class PriceSheetEditGridViewModel
{
public IEnumerable<PriceSheetRowViewModel> Rows { get; set; }
public IEnumerable<PriceSheetColumnViewModel> Columns { get; set; }
}
public class PriceSheetColumnViewModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class PriceSheetRowViewModel
{
public int RowNo { get; set; }
public string Description { get; set; }
public double?[] Prices { get; set; }
}
the view can be like this (part of view.cshtml file...) :
....
#model PriceSheetEditGridViewModel
...
columns.Bound(o => o.Description ).Width(150);
int i = 0;
foreach (var col in Model.Columns)
{
columns
.Bound(model => model.Prices).EditorTemplateName("PriceSheetCellPrice").EditorViewData(new { ColumnId = i })
.ClientTemplate("<span><#=Prices ? jsHelper.addCommas(Prices[" + i.ToString() + "]):null#></span>")
.Title(col.Title).Width(80);
i++;
}
....
and the PriceSheetCellPrice.cshtml editor template file (in shared\editortemplates folder) :
#model decimal?
#(Html.Telerik().NumericTextBox()
.Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)+"["+ViewBag.ColumnId+"]")
.InputHtmlAttributes(new { style = "width:100%" })
})
.EmptyMessage("")
.DecimalDigits(0)
.DecimalSeparator(",")
.MinValue(0)
.Value((double?) Model)
)

Related

How and where to use AddRange() method

I want to display related data from second table with each value in first table
i have tried this query
public ActionResult Index()
{
List<EmployeeAtt> empWithDate = new List<EmployeeAtt>();
var employeelist = _context.TblEmployee.ToList();
foreach (var employee in employeelist)
{
var employeeAtt = _context.AttendanceTable
.GroupBy(a => a.DateAndTime.Date)
.Select(g => new EmployeeAtt
{
Date = g.Key,
Emp_name = employee.EmployeeName,
InTime = g.Any(e => e.ScanType == "I") ? g.Where(e =>
e.ScanType == "I").Min(e =>
e.DateAndTime.ToShortTimeString())
.ToString() : "Absent",
OutTime = g.Any(e => e.ScanType == "O") ? g.Where(e =>
e.ScanType == "O").Max(e =>
e.DateAndTime.ToShortTimeString())
.ToString() : "Absent"
});
empWithDate.AddRange(employeeAtt);
}
return View(empWithDate);
}
Here is my attendance Table
AttendanceTable
Results
I want to display the shortest time with "I" Column value against each employee and last time with "O" Column value as out time. I think i am not using AddRange() at proper place. Where it should go then?
public partial class TblEmployee
{
public TblEmployee()
{
AttendanceTable = new HashSet<AttendanceTable>();
}
public int EmpId { get; set; }
public string EmployeeName { get; set; }
public virtual ICollection<AttendanceTable> AttendanceTable { get; set; }
}
public partial class AttendanceTable
{
public int Id { get; set; }
public int AttendanceId { get; set; }
public int EmployeeId { get; set; }
public string ScanType { get; set; }
public DateTime DateAndTime { get; set; }
public virtual TblEmployee Employee { get; set; }
}
The actual problem is not related to AddRange(), you need a where clause before GroupBy() to limit attendances (before grouping) to only records related to that specific employee, e.g.
_context.AttendanceTable
.Where(a => a.Employee == employee.EmployeeName)
.GroupBy(a => a.DateAndTime.Date)
...
Depended on your model, it is better to use some kind of ID instead of EmployeeName for comparison if possible.
Also you can use SelectMany() instead of for loop and AddRange() to combine the results into a single list. like this:
List<EmployeeAtt> empWithDate = _context.TblEmployee.ToList()
.SelectMany(employee =>
_context.AttendanceTable
.Where(a => a.Employee == employee.EmployeeName)
.GroupBy(a => a.DateAndTime.Date)
.Select(g => new EmployeeAtt
{
...
})
);
...

Get first object in child collection inside projection

I have the following entities:
public class Mark {
public Int32 Id { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<MarkLocalized> MarksLocalized { get; set; }
} // Mark
public class MarkLocalized {
public Int32 Id { get; set; }
public String Culture { get; set; }
public String Name { get; set; }
public virtual Mark Mark { get; set; }
} // MarkLocalized
Given a culture, for example "en", I need to get all Marks, their localized name for the given culture and map them to a MarkModel:
public class MarkModel {
public Int32 Id { get; set; }
public String Name { get; set; }
} // MarkModel
I tried the following:
Context context = new Context();
context.Marks
.SelectMany(x => x.MarksLocalized, (y, z) =>
new MarkModel {
Id = y.Id,
Name = z.Name,
Slug = z.Slug
});
But I need only the MarksLocalized which culture is equal to the given culture.
How can I do this?
Thank you,
Miguel
You can add Where inside SelectMany:
Context context = new Context();
context.Marks
.SelectMany(x => x.MarksLocalized.Where(x => x.Culture == "en"), (y, z) =>
new MarkModel {
Id = y.Id,
Name = z.Name,
Slug = z.Slug
});
But should be more clear with syntax query:
from m in new Context.Marks
from l in m.MarksLocalized
where l.Culture == "en"
select new MarkModel { m.ID, m.Name, l.Slug }
which should be translated into following query by compiler:
context.Marks
.SelectMany(x => x.MarksLocalized, (m, l) => new { m, l })
.Where(x => x.l.Culture == "en")
.Select(x => new MarkModel { x.m.ID, x.m.Name, x.l.Slug })
which should produce exact same results as first one.

KendoUI Scheduler

I am trying to implement the scheduler control to show live calculation of material usage on daily bases (in a week by week view).
I am unable to have the Usage data displayed in the cells although I managed to have to Materials displayed on the left hand side. I wonder if any one could help or give me some hints on what I am doing wrong. Here is my code so far:
I can be sure that data is being return to the view but the view is not showing the usage data, which is simply numeric values corresponding to a material on a specific day of the week. I have also attached a screenshot of how it looks like:
the Controller method to read the data:
public JsonResult Read([DataSourceRequest] DataSourceRequest request)
{
try
{
var usageList = new List<ReportUsageViewModel>();
var imports = _importRespository.GetImports();
foreach (var usageReportStoredProcedure in imports)
{
var usageViewModel = new ReportUsageViewModel();
usageViewModel.MaterialID = usageReportStoredProcedure.MaterialID;
usageViewModel.Start = usageReportStoredProcedure.ScanDate;
usageViewModel.End = usageReportStoredProcedure.ScanDate;
usageViewModel.DailyUsage = usageReportStoredProcedure.UsageQuantity;
usageViewModel.Title = usageReportStoredProcedure.UsageQuantity.ToString();
usageList.Add(usageViewModel);
}
return Json(usageList.ToDataSourceResult(request));
}
catch (Exception exc)
{
ErrorHelper.WriteToEventLog(exc);
return null;
}
}
The actual control
<div id="StockViewer">
#(Html.Kendo().Scheduler<WorcesterMarble.ViewModels.ReportUsageViewModel>()
.Name("StockViewer")
.Timezone("Europe/London")
.Resources(resource => resource.Add(m => m.MaterialID)
.Title("Materials")
.Name("Materials")
.DataTextField("Name")
.DataValueField("MaterialID")
.BindTo(Model.MaertiaList))
.MajorTick(270)
.MinorTickCount(1)
.StartTime(DateTime.Now.Date.AddHours(8))
.EndTime(DateTime.Now.Date.AddHours(17))
.AllDaySlot(false)
.Date(DateTime.Now.Date)
.Editable(false)
.Views(x => x.WeekView(v =>
{
v.Footer(false);
v.Selected(true);
v.DateHeaderTemplate("<span class='k-link k-nav-day'>#=kendo.toString(date, 'ddd dd/M')#</span>");
}))
.Group(group => group.Resources("Materials").Orientation(SchedulerGroupOrientation.Vertical))
.DataSource(d => d
.Model(m => {
m.Id(f => f.MaterialID);
m.Field(f => f.Title).DefaultValue("No title");
})
.Read("Read", "ReportUsage")
)
)
Update: This is the ViewModel implementing the ISchedulerEvent
public class ReportUsageViewModel : ISchedulerEvent
{
public int MaterialID { get; set; }
public string MaterialName { get; set; }
public int? DailyUsage { get; set; }
public List<MaterialViewModel> MaertiaList { get; set; }
public string Description { get; set; }
public System.DateTime End { get; set; }
public bool IsAllDay { get; set; }
public string RecurrenceException { get; set; }
public string RecurrenceRule { get; set; }
public System.DateTime Start { get; set; }
public string Title { get; set; }
}
The issue was in these two lines:
.StartTime(DateTime.Now.Date.AddHours(8))
.EndTime(DateTime.Now.Date.AddHours(17))
The data was there but these two lines were hiding it. The data was being logged at times outside the time range of 8 to 17 so I removed these two lines and then set the Major ticks to 1440, which is the total number of ticks in 24 hrs, which helped me hiding the time column as I didn't need it..

Summing in multi-level relationship

Using EF code first, I have the following 4 entities
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class Location {
public int Id { get; set; }
public string Name { get; set; }
}
public class InventoryAdjustment {
public int Id { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<AdjustmentLine> Lines { get; set; }
}
public class AdjustmentLine {
public int Id { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
}
What I am trying to do is to get the sum of all inventory adjustments for each item at each location using minimal database round-trips.
The best I achieved so far is:
using (var db = new InventoryContext()) {
var items = db.Items.ToList();
var locations = db.Locations.ToList();
foreach (var item in items) {
Console.WriteLine(item.Name+":");
foreach (var location in locations) {
Console.Write("\t" + location.Name + ": ");
var qty = db.InventoryAdjustments
.Where(p => p.Location.Id == location.Id)
.SelectMany(p => p.Lines)
.Where(p => p.Item.Id == item.Id)
.Select(p => (int?)p.Quantity)
.Sum();
Console.WriteLine(qty ?? 0);
}
}
Console.Read();
}
The above outputs:
Item1:
Location1: 2
Location2: 12
Location3: 21
Item2:
Location1: 4
Location2: 0
Location3: 0
Item3:
Location1: 1
Location2: 17
Location3: 0
But with 3 items and 3 locations in the database, the above code causes 11 calls to the database. 2 for getting items and locations, and 9 for calculating the sum of quantity.
Is there a better way to get the sum with the least amount of round-trips?
This should probably work:
using (var db = new InventoryContext())
{
var items = db.Items.ToList();
var locations = db.Locations.ToList();
items
.Select(item =>
{
Console.WriteLine(item.Name + ":");
return item;
})
.SelectMany(item => locations.Select(location => new { item, location }))
.GroupJoin(
db.InventoryAdjustments
.SelectMany(
inventoryAdjustment => inventoryAdjustment.Lines.Select(
adjustmentLine => new { key = new { locationId = inventoryAdjustment.Location.Id, itemId = adjustmentLine.Item.Id }, adjustmentLine.Quantity }
)
),
x => new { locationId = x.location.Id, itemId = x.item.Id },
y => y.key,
(x, y) =>
{
Console.WriteLine("\t {0}: {1}", x.location.Name, y.Sum(a => a.Quantity));
return 0;
}
).ToList();
Console.Write("\nPress any key...");
Console.ReadKey();
}

Insertion of a new item in grid fails due to client template existence

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> #" +
" } " +
"} #"

Resources