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 ...
}
}
Related
I have a string field IsEnabled, it’s string. Value can be Yes, No or null. I am binding this column to grid column. It’s working as expected. But I want to show this on UI as checkbox. For value Yes, it should be checked or No or null it should be unchecked. And user can check/uncheck, based on user’s action. Yes or NO will be inserted in database.
I couldn’t find proper way of doing this, so what is the best way to handle this scenario?
I have tried by by adding one more bool field and setting it based on value Yes, No or null. And binding this field to grid.
But I am looking for a clean approach
You can use the column template (documentation) to return a checkbox where the value is checked when the record's IsEnabled is Yes.
Then in the dataBound event of the grid (documentation) you would setup the event binding for the checkbox to get when the value changed.
Here is an example:
$('#grid').kendoGrid({
columns: [
{
field: 'Name'
},
{
field: 'name',
template: function(dataItem) {
var checkbox = $('<input />', {
checked: dataItem.IsEnabled === 'Yes',
type: 'checkbox'
});
return checkbox.prop('outerHTML');
}
}
],
dataSource: [
{
Id: 1,
Name: 'Person1',
IsEnabled: 'Yes'
},
{
Id: 2,
Name: 'Person2',
IsEnabled: 'No'
},
{
Id: 3,
Name: 'Person3',
IsEnabled: 'No'
}
],
dataBound: function(e) {
e.sender.table.on('change', 'input[type="checkbox"]', function() {
var checked = this.checked;
// send your AJAX request
});
}
});
Fiddle: https://dojo.telerik.com/igiTASAc
As I was looking for a Kendo MVC solution, So I have implemented it like the below.
Declare the property like this.
[UIHint("DropDownTemplate")]
public IsAllowedCls IsAllowed { get; set; }
public class IsAllowedCls
{
public int IsAllowedKey { get; set; }
public string IsAllowedValue { get; set; }
}
Add view under Views\Shared\EditorTemplates create the view named as DropDownTemplate with below content
#model FxTrader.Models.IsAllowedCls
#(Html.Kendo().DropDownList()
.Name("DropDownTemplate")
.DataValueField("IsAllowedKey")
.DataTextField("IsAllowedValue")
.BindTo((System.Collections.IEnumerable)ViewData["IsAllowedData"])
)
In the controller action method, add the below code.
ViewData["IsAllowedData"] = new List<IsAllowedCls>() { new IsAllowedCls { IsAllowedKey = 1, IsAllowedValue = "Yes" },
new IsAllowedCls { IsAllowedKey = 0, IsAllowedValue = "No" } };
I'm using Alpine to display a list of items that will change. But I can't figure out how to tell Alpine to refresh the list of items once a new one comes back from the server:
<div x-data=" items() ">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
The first "batch" of items is fine, because they're hard-coded in the items() function:
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
]
};
}
Some code outside of Alpine fetches and receives a completely new list of items, that I want to display instead of the original set. I can't figure out how, or if it's even currently possible. Thanks for any pointer.
There are 3 ways to solve this.
Move the fetch into the Alpine.js context so that it can update this.items
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
],
updateItems() {
// something, likely using fetch('/your-data-url').then((res) => )
this.items = newItems;
}
};
}
(Not recommended) From your JavaScript code, access rootElement.__x.$data and set __x.$data.items = someValue
<script>
// some other script on the page
// using querySelector assumes there's only 1 Alpine component
document.querySelector('[x-data]').__x.$data.items = [];
</script>
Trigger an event from your JavaScript and listen to it from your Alpine.js component.
Update to the Alpine.js component, note x-on:items-load.window="items = $event.detail.items":
<div x-data=" items() " x-on:items-load.window="items = $event.detail.items">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
Code to trigger a custom event, you'll need to fill in the payload.
<script>
let event = new CustomEvent("items-load", {
detail: {
items: []
}
});
window.dispatchEvent(event);
</script>
Expanding on Hugo's great answer I've implemented a simple patch method that lets you update your app's state from the outside while keeping it reactive:
<div x-data="app()" x-on:patch.window="patch">
<h1 x-text="headline"></h1>
</div>
function app(){
window.model = {
headline: "some initial value",
patch(payloadOrEvent){
if(payloadOrEvent instanceof CustomEvent){
for(const key in payloadOrEvent.detail){
this[key] = payloadOrEvent.detail[key];
}
}else{
window.dispatchEvent(new CustomEvent("patch", {
detail: payloadOrEvent
}));
}
}
};
return window.model;
}
In your other, non-related script you can then call
window.model.patch({headline : 'a new value!'});
or, if you don't want assign alpine's data model to the window, you can simply fire the event, as in Hugo's answer above:
window.dispatchEvent(new CustomEvent("patch", {
detail: {headline : 'headline directly set by event!'}
}));
Kendo UI v2015.2.805
I have a KendoGrid with a template column that does a conditional to determine if a set of buttons should be added, if so additional evaluations are needed, and I can't figure out how to nest them.
The below works but does not have the required additional evaluation:
{ field: "Served", title: "Served",
template: "<div>" +
"#= (Kind==0 || Kind==7) ? '" +
"<button type=\"button\" data-id=\"12345\">Yes</button>" +
"<button type=\"button\" data-id=\"54321\">No</button>" +
"' : " +
"'NO BUTTON HERE'" +
"#</div>"
I multi-lined it to try to get it to look good, which it does not. The idea is that if the Kind = 0 or 7 then show two buttons otherwise do not. Works great.
However I need to evaluate the data-id to #= ID #, so I try:
" <button type=\"button\" data-id=\"' #= ID # '\">Yes</button>"
I know I need to 'drop out' of the quoted string to get the evaluation to work and since I have used double quotes for the whole expression I am returning the button in the conditional as a single quoted string, and as such escaping the button attributes, but I can't get it to evaluate the #=.
I've tried so many different combinations I've lost track.
So what is the 'right-way' to do this?
A SOLUTION:
Accepting David's answer with a modification to use template evaluation in the function:
{ field: "Served", title: "Served",
template: function (data) {
switch (data.Kind) {
case 0:
case 7:
var template = kendo.template("<button type='button' data-id='#= ID #' >Yes</button><button type='button' data-id='#= ID #'>No</button>");
return template(data);
default:
return '';
}
}
Having the function perform the initial test removes one level and allows 'normal' evaluation to occur.
You can use a function instead I Beleive it will would make things so much easier for you.
your template can be "#= buildButtons(data) #"
function buildButtons(model) {
if (model.Kind == 0 || model.Kind == 7) {
return "hello world";
}
return "";
}
here is a code sample
https://dojo.telerik.com/UQuqAfuv
<div id="grid"></div>
<script>
var people = [
{ id: 1, firstName: 'David', lastName: 'Lebee' },
{ id: 2, firstName: 'John', lastName: 'Doe' }
];
$('#grid').kendoGrid({
dataSource: {
transport: {
read: function(options) {
options.success(people);
}
}
},
columns: [
{ field: 'firstName', title: 'First Name' },
{ field: 'lastName', title: 'Last Name' },
{ title: 'Actions', template: '#= buildActions(data) #'}
]
});
function buildActions(model) {
if (model.firstName == "David") {
return 'Hello David';
}
return '';
}
</script>
I am having a hard time trying to joing a filterBy with orderBy, on vuejs 2.0, with all research I have found about this subject, as of link on the bottom of my question.
This is my filter, which is working:
// computed() {...
filteredResults() {
var self = this
return self.results
.filter(result => result.name.indexOf(self.filterName) !== -1)
}
A method called in the component:
// methods() {...
customFilter(ev, property, value) {
ev.preventDefault()
this.filterBook = value
}
In the component:
// Inside my component
Name..
And another filter, which works as well:
// computed() {...
orderByResults: function() {
return _.orderBy(this.results, this.sortProperty, this.sortDirection)
}
To comply with my orderBy I have this method:
// methods() {...
sort(ev, property) {
ev.preventDefault()
if (this.sortDirection == 'asc' && this.sortProperty == property ) {
this.sortDirection = 'desc'
} else {
this.sortDirection = 'asc'
}
this.sortProperty = property
}
And to call it I have the following:
// Inside my component
Name..
I have found in the docs how we use this OrderBy, and in this very long conversation how to use filter joint with sort, but I could really not implement it...
Which should be some like this:
filteredThings () {
return this.things
.filter(item => item.title.indexOf('foo') > -1)
.sort((a, b) => a.bar > b.bar ? 1 : -1)
.slice(0, 5)
}
I could not make this work...
I tried in many forms as of:
.sort((self.sortProperty, self.sortDirection) => this.sortDirection == 'asc' && this.sortProperty == property ? this.sortDirection = 'desc' : this.sortDirection = 'asc' )
But still, or it does not compile or it comes with errors, such as:
property not defined (which is defines such as I am using it in the other method)
method of funcion not found (is happens when comment my method sort.. maybe here is what I am missing something)
Thanks for any help!
The ideas of your approach seem valid, but without a full example it's hard to tell what might actually be wrong.
Here's a simple example of sorting and filtering combined. The code can easily be extended e.g. to work with arbitrary fields in the test data. The filtering and sorting is done in the same computed property, based on the parameters set from the outside. Here's a working JSFiddle.
<div id="app">
<div>{{filteredAndSortedData}}</div>
<div>
<input type="text" v-model="filterValue" placeholder="Filter">
<button #click="invertSort()">Sort asc/desc</button>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
testData: [{name:'foo'}, {name:'bar'}, {name:'foobar'}, {name:'test'}],
filterValue: '',
sortAsc: true
};
},
computed: {
filteredAndSortedData() {
// Apply filter first
let result = this.testData;
if (this.filterValue) {
result = result.filter(item => item.name.includes(this.filterValue));
}
// Sort the remaining values
let ascDesc = this.sortAsc ? 1 : -1;
return result.sort((a, b) => ascDesc * a.name.localeCompare(b.name));
}
},
methods: {
invertSort() {
this.sortAsc = !this.sortAsc;
}
}
});
</script>
function bind_single_select() {
if (!$("input#single").length > 0) {
$("span.single_select").prepend("<input type='checkbox' name='single' id='single' checked='checked' style='vertical-align:middle' />");
}
$("table#gridTable").find("tr").click(function () {
if ($("input#single").attr("checked")) {
$(".trSelected").removeClass("trSelected");
$(this).addClass("trSelected");
}
});
}
I found this in Flexigrid but in JQGrid How can I do it.
Another Question :
.navButtonAdd('#pager',
{ caption: "Add",
buttonimg: "/Areas/Pages/Content/Images/add.png",
onClickButton: function () {
PopupCenter('<%= Url.Action("CreatePublisher","Publisher") %>',
'CreatePublisher', '500', '300');
}, position: "last"
})
the buttonimg is not work even I use
ui-icon-plus
Thanks in Advance.
The second part of your question is very easy to answer. The parameter buttonimg is no more supported in the navButtonAdd function. You should use buttonicon instead. An example you can find here. In general as a value of buttonicon you can use any from the jQuery UI Framework Icons.
To toggle multipleSearch parameter you can just define search parameters of navGrid separately and toggle the value of the multipleSearch property. To make all more easy I suggest to use an additional parameter recreateFilter:true.
var grid = jQuery('#list');
var pSearch = { multipleSearch:false, recreateFilter:true };
grid.jqGrid({
// all jqGrid parameters
}).jqGrid ('navGrid', '#pager', {edit:false, add:false, del:false, refresh:true, view:false},
{},{},{},pSearch));
$("#pager_left table.navtable tbody tr").append ( // here 'pager' part or #pager_left is the id of the pager
'<td><div><input type="checkbox" class="myMultiSearch" id="navMultiSearch"/>Multi-Search</div></td>');
$(".myMultiSearch").change(function() {
if ($(this).is(':checked')) {
pSearch.multipleSearch = true;
$(".myMultiSearch").attr("checked","checked");
}
else {
pSearch.multipleSearch = false;
$(".myMultiSearch").removeAttr("checked");
}
});
On the small demo I inserted both internal and external checkboxes to the navigation bar and a custom button additionally: