I am trying to create a class that create a generic kendo TreeView that the tree can have items with checkbox and items without checkbox.
So, I created a class with the flowing c'tor:
constructor(checkable: boolean = false) {
// Create the treeview options
const treeViewOptions: kendo.ui.TreeViewOptions = {
checkboxes: {
checkChildren: true,
template: "# if (item.level() > 0) { #" +
"<input type='checkbox' #= item.checked ? 'checked' : '' #>" +
"# } #"
},
// ... The rest of the treeViewOptions ...
}
Now, all items that their item.level==0 are without checkbox.
I want that if the parameter "checkable" of the c'tor is false, than all the items in the tree will not have checkboxes. I didn't know how to pass the "checkable" parameter into the template. I wanted something like this:
checkboxes: {
checkChildren: true,
template: "# if (checkable && item.level() > 0) { #" +
"<input type='checkbox' #= item.checked ? 'checked' : '' #>" +
"# } #"
},
Please help me with that and if you think that there is more elegant way to do that I will be happy to hear.
Thanks
You could make the template be an anonymous function and have it emit different template strings depending on constructor argument.
template: function () {
if (checkable) {
return ... template string that allows checkboxes at item level > 0 ...
} else {
return ... simpler template string that has no checkboxes anywhere ...
}
}
Seems to be the usual problem with Kendo grids, but a dropdown being rendered into the toolbar needs to fire an Ajax request to the server and refresh the grid from the returned data. I can see in Fiddler that the Ajax call is successfully being actioned and data is definitely being returned but we're not getting anything refreshed on the grid.
Here's the View code:
<div class="grid-validation-error" id="unitgrid-validation-error">
</div>
#(Html.Kendo()
.Grid(Model)
.Name("WheelchairAlertsGrid")
.Sortable()
.Scrollable(scr => scr.Height("100%"))
.Filterable()
.ToolBar(t => t.Template(
#<text>
<div class="toolbar">
<label class="category-label" for="category">Show alerts for:</label>
#(Html.Kendo().DropDownList()
.Name("filter-periods")
.DataTextField("Text")
.DataValueField("Value")
.OptionLabel("Month")
.Events(e => e.Change("filterPeriodChange"))
.BindTo(new List<SelectListItem>(){
new SelectListItem{ Text = "Day", Value = "Day" },
new SelectListItem{ Text = "Week", Value = "Week" },
new SelectListItem{ Text = "Month", Value = "Month" } })
)
</div>
</text>
))
.Pageable(paging => paging.Messages(msg => msg.Display(ResourceManager.RetrieveResource("PagingFormat"))))
.Columns(
col =>
{
col.Bound(um => um.SerialNumber).Width(150).Title("Wheelchair").ClientTemplate
(
"<a href='" +
Url.DealerGroupAction("Index", "Wheelchair") +
"/#= WheelchairDataAssignmentId #'>#= SerialNumber #" + "</a>"
);
col.Bound(um => um.Name).Width(150);
col.Bound(um => um.ChargeAlert).Width(60);
col.Bound(um => um.BatteryAbuse).Width(60);
col.Bound(um => um.Flash).Width(60);
col.Bound(um => um.Transmission).Width(60);
col.Bound(um => um.DealerGroup).Width(100);
})
)
And here's the JS code to refresh the data (with assorted variations commented out that have also been tried but failed to yield results):
function filterPeriodChange(e) {
var ddl = $('#filter-periods').data('kendoDropDownList');
var grid = $('#WheelchairAlertsGrid').data("kendoGrid");
$.getJSON('#Url.DealerGroupWheelChairAlertsApiUrl("WheelchairAlerts")', { filterPeriod: ddl.value() }, function (data) {
grid.dataSource = data;
});
}
There's always something really simple causing these sorts of problems but I can't see the forest for the trees. Any assistance appreciated.
Cracked it.
The Kendo Grid data source needs to be told to expect Ajax content. So the code should look like the following:
... other stuff as above
.DataSource(ds => ds
.Ajax()
.PageSize(20)
)
The next piece of the puzzle is to ensure the data source is being set correctly after picking up the data:
function filterPeriodChange(e) {
var ddl = $('#filter-periods').data('kendoDropDownList');
var grid = $('#WheelchairAlertsGrid').data("kendoGrid");
$.getJSON('#Url.DealerGroupWheelChairAlertsApiUrl("WheelchairAlerts")', { filterPeriod: ddl.value() }, function (data) {
var dataSource = new kendo.data.DataSource({
data: data.Data,
pageSize: 20
});
grid.setDataSource(dataSource);
});
}
That seems to have sorted the issue. Now, changing my dropdown list at the top level calls my filterPeriodChange method, fires off an Ajax request and re-binds the data.
I have a kendo grid, in which I want to make a column editable.
Check the below code what I have done
Html.Kendo().Grid(Model.MyGridData)
.Name("MyGridName")
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Events(e => e.RequestEnd("FormatDates"))
.ServerOperation(true))
.Columns(columns =>
{
foreach (var item in Model.GridHeaderCollection)
{
if (item.IsEditable)
{
columns.Bound(item.Name)
.ClientTemplate("# if(" + item.Name + "){#<input id='checkbox' onclick='grdChkBoxClick(this); ' class='chkboxApprove' type='checkbox' checked='checked' value='#=" + item.Name + "#' chkValue='decline_#=" + item.Name + "#' />#}else{#<input id='checkbox' onclick='grdChkBoxClick(this); ' class='chkboxApprove' type='checkbox' value='#=" + item.Name + "#' chkValue='decline_#=" + item.Name + "#' />#}# ")
.Title(item.Name);
}
else
{
columns.Bound(item.Name).Title(item.DisplayName).Format(GridDataFormat);
}
}
}).Events(e => e.DataBound("AttachmentActionFlyOutBound"))
So when I check/ uncheck the checkbox it should reflect in the data source
As I checked Editable is available for the entire model. I need it for a single column.
I have seen some post which uses schema, didn't get how to implement the same here
I am learning CanJS now , so I want to try a very basic small demo. The demo is you will have different types of mobile recharge plans which displayed on the top (Radio buttons) and by choosing each plan the corresponding price options will be displayed in a table at the bottom.
For this demo I create two Model , 2 Control and 2 Template files , my question is how can two control communicate with each other? What is the standard way?
For now I am directly calling the control method through its instance , but I am not sure if it is right way to do. Also please explain Can.Route.
Output
http://jsfiddle.net/sabhab1/2mxfT/10/
Data
var CATEGORIES = [{id: 1 , name: "2G Internet Recharge"},
{id: 2 , name: "3G Internet Recharge"},
{id: 3 , name: "full talktime Recharge"},
{id: 4 , name: "Validity and talktime Recharge"},
{id: 5 , name: "National and international roaming"}];
var RECHARGEAMOUNTS =[{
id: 1 ,
values : [{amount: "Rs. 100" , benefit:"300 MB" ,validity:"30"},
{amount: "Rs. 200" , benefit:"1 GB" ,validity:"30"}]
},
{
id: 2 ,
values : [{amount: "Rs. 10" , benefit:"300 MB" ,validity:"30"},
{amount: "Rs. 99" , benefit:"100 GB" ,validity:"90"}]
},
{
id: 3 ,
values : [{amount: "Rs. 80" , benefit:"1 GB" ,validity:"50"},
{amount: "Rs. 99" , benefit:"100 GB" ,validity:"50"}]
},
{
id: 4 ,
values : [{amount: "Rs. 55" , benefit:"30 MB" ,validity:"10"},
{amount: "Rs. 200" , benefit:"1 GB" ,validity:"30"},
{amount: "Rs. 99" , benefit:"100 GB" ,validity:"90"}]
},
{
id: 5 ,
values : [{amount: "Rs. 880" , benefit:"100 MB" ,validity:"90"},
{amount: "Rs. 550" , benefit:"2 GB" ,validity:"30"},
{amount: "Rs. 1000" , benefit:"4 GB" ,validity:"90"},
{amount: "Rs. 1550" , benefit:"10 GB" ,validity:"90"}]
}
];
Model
//Model Category
CategoryModel = can.Model({
findAll : function(){
return $.Deferred().resolve(CATEGORIES);
}
},{});
//Model Category
ReachargeAmountModel = can.Model({
findAll : function(){
return $.Deferred().resolve(RECHARGEAMOUNTS);
},
findOne : function(params){
return $.Deferred().resolve(RECHARGEAMOUNTS[(+params.id)-1]);
}
},{});
Control
**// Can Control
var CategoryControl = can.Control({
// called when a new Todos() is created
init: function (element, options) {
// get all todos and render them with
// a template in the element's html
var el = this.element;
CategoryModel.findAll({}, function (values) {
el.html(can.view('categoriesEJS', values))
});
this.options.rchAmtCtrl = new RechargeAmountControl("#rechnageAmountView");
},
'input click' : function( el, ev ) {
var id = el.data('category').attr('id');
console.log(id);
this.options.rchAmtCtrl.update(id);
}
});
// Can Control
var RechargeAmountControl = can.Control({
// called when a new Todos() is created
init: function (element, options) {
// get all todos and render them with
// a template in the element's html
this.update(1);//this.update(id,this.element);
},
update : function(id){
var el = this.element;
ReachargeAmountModel.findOne({id: id}, function( rechargeAmount ){
// print out the todo name
//console.log(rechargeAmount.values[id].attr('benefit'));
el.html(can.view('RechnageAmountEJS', rechargeAmount.values));
});
}
});**
View
<form id='categoriesView'></form>
</p>
<table id='rechnageAmountView'></table>
<script type='text/ejs' id='RechnageAmountEJS'>
<tr>
<th>Recharge Amount</th>
<th>Benefits</th>
<th>Validity(Days)</th>
</tr>
<% this.each(function( rechargeAmount ) { %>
<tr>
<td>
<%= rechargeAmount.attr( 'amount' ) %>
</td>
<td>
<%= rechargeAmount.attr( 'benefit' ) %>
</td>
<td>
<%= rechargeAmount.attr( 'validity' ) %>
</td>
</tr>
<% }) %>
</script>
<script type='text/ejs' id='categoriesEJS'>
<% this.each(function( category ) { %>
<input type="radio"
name="category"
<%= category.attr('id') == 1 ? 'checked' : '' %>
value=<%= category.attr( 'name' ) %>
<%= (el) -> el.data('category',category) %>>
<%= category.attr( 'name' ) %>
</input>
<% }) %>
</script>
Main Call
new CategoryControl("#categoriesView");
There are several ways for doing this.
1. Calling methods directly
This is what you are doing and isn't necessarily wrong. To make things a little more flexible you could pass the class or instance of RechargeAmountControl when initializing CategoryControl instead of using it directly.
2. DOM events
Here it goes a little more into event oriented architecture. If you generally want to notify other Controls you can just trigger any kind of event and make them listen to it. Something like this: http://jsfiddle.net/2mxfT/11/
' rechargeAmountUpdated': function(element, event, id){
var el = this.element;
console.log(arguments);
ReachargeAmountModel.findOne({id: id}, function( rechargeAmount ){
// print out the todo name
//console.log(rechargeAmount.values[id].attr('benefit'));
el.html(can.view('RechnageAmountEJS', rechargeAmount.values));
});
}
3. Observables
Another option is to use Observables to maintain shared state. This is a great way to focus on the data and let live binding do all the rest. To make things more flexible, the state object should be passed during Control initialization (see http://jsfiddle.net/2mxfT/12/):
var state = new can.Observe();
new RechargeAmountControl("#rechnageAmountView", {
state: state
});
new CategoryControl("#categoriesView", {
state: state
});
state.attr('rechargeId', 1);
And then you can just listen to attribute changes in the RechargeAmountControl like this:
'{state} rechargeId': function(Construct, event, id){}
This handler will be called whenever you update your state Observe.
And this is also where can.route comes in. Basically can.route is an Observe that saves its state in the location hash. In the above example like #!&rechargeId=1 (unless you initialize a specific route like can.route(':rechargeId')). If the location hash changes the Observe will be updated and vice versa.
I developed a simple plugin to sent request to a JSON text file, retrieve a data containing image list and append the html in the element calling the plugin.
Now, the code is working fine while it is running on only one element, but when I am using it on more than one element, now the elements get loaded on the last element calling the plugin only.
Plugin Code:
$.fn.testPlugin = function(param) {
var options = {
url : "",
pause : 2000,
callback : 5000
};
this.each(function() {
elementId = $(this).attr("id");
options = $.extend(options,param);
if(options.url=="") { console.log("URL is not specified"); }
else {
$.post(
options.url,
function(data) {
$("#"+elementId).html("<ul></ul");
$.each(data.dashboard, function(k,v) {
html = '<li>';
html += '<a href="'+v.TargetUrl+'" target="'+v.Target+'">';
html += '<img src="' + v.ImageUrl + '" alt="' + v.Alt +'" title="' + v.OverlayText +'" />';
html += '</a><p>'+v.OverlayText+'</p></li>';
$("ul",$("#"+elementId)).append(html);
});
},
"json"
);
}
});
}
Plugin Initialization/Execution
$("#this").testPlugin({
url: 'test.txt'
});
$("#that").testPlugin({
url: 'text.txt'
});
HTML
<div id="this" style="border-style: solid;border-color: #09f;padding: 10px;">
This is a sample div.
</div>
<div id="that" style="border-style: solid;border-color: #09f;padding: 10px;">
Another div
</div>
UPDATE
I also found out that the problem is happening due to AJAX request. If I create static list and then append it on the div, this time this works with any number of instantiation. A demo of static call is here. Now, help me do the same by retrieving the list from an AJAX request.
UPDATE 2
Thanks to Groovek, Here is a demo of the actual problem. You can notice, that the elements of both requests are append to the last element.
You're assigning to elementID without declaring it. This means it'll be a global variable, which means it'll always be equal to the last thing assigned to it (in this case, #that). If you just keep a local reference to the element, the code will work. Here's a modified jsfiddle: http://jsfiddle.net/s9BDT/
Code inside the each 'loop':
var el = $(this);
el.html("<ul></ul>");
options = $.extend(options,param);
if(options.url=="") { console.log("URL is not specified"); }
else {
$.post(
//options.url,
"/echo/json/",
{json:'{"dashboard":[{"TargetUrl":"toto.html", "Alt" : "sample 1", "ImageUrl":"toto.png", "OverlayText":"toto"},{"TargetUrl":"titi.html", "Alt" : "sample 2", "ImageUrl":"titi.png", "OverlayText":"titi" }]}'},
function(data) {
//alert(data.dashboard);
html = '';
$.each(data.dashboard, function(k,v) {
html += '<li>';
html += '<a href="'+v.TargetUrl+'" target="'+v.Target+'">';
html += '<img src="' + v.ImageUrl + '" alt="' + v.Alt +'" title="' + v.OverlayText +'" />';
html += '</a><p>'+v.OverlayText+'</p></li>';
});
//alert(html);
$("ul",el).append(html);
},
"json"
);
}