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>
Related
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 ...
}
}
I'm trying to populate default value for dependent dropdown using knockout.
When values are harcoded it works, but I need to get values from ajax request and then, the second dropdown option is not updated. The value self.selectedState is updated but I guess that as I haven't already the options populated, then value in select is not bind. This is my code so far:
function myViewModel(country, state) {
var self = this;
self.selectedCountry = ko.observable();
self.selectedState = ko.observable();
self.availableCountries = ko.observableArray([
{
id: 1, name: 'United States', states: [
{ id: 1, name: 'Alabama' },
{ id: 2, name: 'California' },
]
},
{
id: 2, name: 'Canada', states: [
{ id: 53, name: 'Alberta' },
]
}
]);
self.availableStates = ko.observableArray([]);
self.selectedCountry.subscribe(function() {
self.availableStates([]);
for (var i = 0; i < self.availableCountries().length; i++) {
if (self.availableCountries()[i].id == self.selectedCountry()) {
self.availableStates(self.availableCountries()[i].states);
break;
}
}
});
self.selectedCountry(1).selectedState(2);
}
var viewModel = new myViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: availableCountries, optionsText: 'name', optionsValue: 'id', optionsCaption: 'Select a country...',
value: selectedCountry"></select>
<select data-bind="options: availableStates, optionsText: 'name',optionsValue: 'id', value: selectedState, visible: availableStates().length > 0" style="display:none"></select>
Is there something special that needs to be done when options are obtained from ajax?
jsfiddle
The issue isn't AJAX, specifically, but the fact that self.selectedState doesn't have a corresponding option for some time (while the options are being fetched).
From the docs:
Normally, when you use the value binding on a <select> element, it
means that you want the associated model value to describe which item
in the <select> is selected. But what happens if you set the model
value to something that has no corresponding entry in the list? The
default behavior is for Knockout to overwrite your model value to
reset it to whatever is already selected in the dropdown, thereby
preventing the model and UI from getting out of sync.
However, sometimes you might not want that behavior. If instead you
want Knockout to allow your model observable to take values that have
no corresponding entry in the <select>, then specify valueAllowUnset:
true. In this case, whenever your model value cannot be represented in
the <select>, then the <select> simply has no selected value at that
time, which is visually represented by it being blank.
This is a perfect opportunity to make use of a computed observable. A computed seems to a good fit for this job because it will save you from setting up your own manual subscriptions at track dependencies for you. Give it a try - I've changed very little of your code to accomplish this..
function myViewModel(country, state) {
var self = this;
self.selectedCountry = ko.observable();
self.selectedState = ko.observable();
self.availableCountries = ko.observableArray([{
id: 1,
name: 'United States',
states: [{ id: 1, name: 'Alabama' },
{ id: 2, name: 'California' }, ]
},
{ id: 2,
name: 'Canada',
states: [{ id: 53, name: 'Alberta' }, ]
}]);
self.availableStates = ko.computed(function() {
var states = [];
for (var i = 0; i < self.availableCountries().length; i++) {
if (self.availableCountries()[i].id == self.selectedCountry()) {
states = states.concat(self.availableCountries()[i].states);
break;
}
}
return states;
});
self.selectedCountry(1).selectedState(2);
}
var viewModel = new myViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: availableCountries, optionsText: 'name', optionsValue: 'id', optionsCaption: 'Select a country...',
value: selectedCountry"></select>
<select data-bind="options: availableStates, optionsText: 'name',optionsValue: 'id', value: selectedState, visible: availableStates().length > 0" style="display:none"></select>
I have asked a variant of this question here. But basically I need to create a computed property that operated on a hasMany association. I need to do sorting similar to the javascript sort function; where I can do something like
files = ["File 5", "File 1", "File 3", "File 2"];
files.sort(function(a,b){
return parseInt(b.split(' ').pop()) - parseInt(a.split(' ').pop())
});
result:
["File 5", "File 3", "File 2", "File 1"]
Here is my jsbin:
http://emberjs.jsbin.com/simayexose/edit?html,js,output
Any help would be greatly appreciated.
Note:
My jsbin presently is not working correctly (for reasons other then this question). I have posted a question about that here. I just did not want to hold up an answer to this question.
Update 1
Thanks #engma. I implemented the instructions. As a matter of fact, I copied and pasted what was posted. This is the new jsbin.
http://emberjs.jsbin.com/roqixemuyi/1/edit?html,js,output
I still do not get anything sorted, though. And even if it did, it still would not have sorted the way I would like it.
I need something like the following: (below are errors that I get when I try to implement this in my code, not from jsbin, since I can not get jsbin to work)
sortedFiles: function(){
return this.get('files').sort(function(a,b){
return parseInt(b.split(' ').pop()) - parseInt(a.split(' ').pop());
});
}.property('files.#each.name')
When I do this I get the following error:
Uncaught TypeError: this.get(...).sort is not a function
So since this.get('files') returns a promise, I figured I would try this;
sortedFiles: function(){
return this.get('files').then(function(files){
return files.sort(function(a,b){
return parseInt(b.split(' ').pop()) - parseInt(a.split(' ').pop());
});
});
}.property('files.#each.name')
But then I get the following error:
Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed {_id: 243, _label: undefined, _state: undefined, _result: undefined, _subscribers: }
BTW, I am using emberjs v1.11.0
And, the sortBy I am using is ember-cli/node_modules/bower-config/node_modules/mout/array/sortBy.js
Here is the code for it
var sort = require('./sort');
var makeIterator = require('../function/makeIterator_');
/*
* Sort array by the result of the callback
*/
function sortBy(arr, callback, context){
callback = makeIterator(callback, context);
return sort(arr, function(a, b) {
a = callback(a);
b = callback(b);
return (a < b) ? -1 : ((a > b) ? 1 : 0);
});
}
module.exports = sortBy;
Update 2
So to answer the question how to do an Emberjs advanced sort hasMany association as a computed property; I had to change
this.get('files').sort(function(a,b){
...
});
return this.get('files').toArray().sort(function(a,b){
...
});
This allowed me to use the javascript sort and return the desired sorted objects.
Ok first of all your JSBin had many issues so lets go throw them one by one
1- you did not include any Ember-Data build, so I included 1, this is needed for the fixtures and the models
<script src="http://builds.emberjs.com/tags/v1.0.0-beta.15/ember-data.js"></script>
2- Your Scripts
var App = window.App = Ember.Application.create({
});
//First this is how to register the adapter
App.ApplicationAdapter = DS.FixtureAdapter.extend({});
App.IndexRoute = Ember.Route.extend({
model: function() {
//Second with find you pass in the ID so I am using 1
//if you want to get all folders use findAll()
return this.store.find('folder',1);
}
});
App.IndexController = Ember.Controller.extend({
});
App.Router.map(function() {
});
App.Folder = DS.Model.extend({
name: DS.attr('string'),
files: DS.hasMany('file',{async:true}),
sortedFiles: function(){
//Sorty By has no second parameter, if you need more sorting power, do it your self
return this.get('files').sortBy('name');
}.property('files.#each.name')
});
App.File = DS.Model.extend({
name: DS.attr('string'),
folder: DS.belongsTo('folder',{async:true})
});
App.File.FIXTURES = [
{
id: 1,
name: 'File 5',
folder:1
},
{
id: 2,
name: 'File 1',
folder:1
},
{
id: 3,
name: 'File 3',
folder:1
},
{
id: 4,
name: 'File 2',
folder:2
},
{
id: 5,
name: 'File 6',
folder:2
},
{
id: 6,
name: 'File 4',
folder:2
}
];
App.Folder.FIXTURES = [
{
id: 1,
name: 'Folder 1',
files:[1,2,3]
},
{
id: 2,
name: 'Folder 2',
files:[4,5,6]
}
];
Your Template:
<div>
Folders: <br>
<ul>
<li>
Name: {{model.name}} <br>
Files:
{{!-- here we access the sorted files property in the model--}}
{{#each file in model.sortedFiles}}
{{file.name}} <br/>
{{/each}}
</li>
</ul>
</div>
I have an ExtJS 4 dataView and i would like to catch the id of a selected node.
It is the first time that i'm using the dataView, then, there are some troubles.
The store is loaded correctly and i see the datas into the view very well. The problem which i'm having, concern the "classic" actions of update and delete, particularly getting the id of a selected item.
For example into a grid i click, then select a record and through a button's pressing i open a window (or other actions) with a loaded form (by sending in AJAX to the store, the id of the selected row) and i update the datas.
I'm not still able to do it with the ExtJS 4 dataView.
Below my dataView:
dataView_player = Ext.create('Ext.Panel', {
id: 'images-view',
frame: true,
collapsible: false,
autoWidth: true,
title: 'Giocatori (0 items selected)',
items: [ Ext.create('Ext.view.View', {
id:'players-view',
store: store_player,
multiSelect: true,
height: 310,
trackOver: true,
overItemCls: 'x-item-over',
itemSelector: 'div.thumb-wrap',
emptyText: 'Nessun giocatore visualizzato',
tpl: [
'<tpl for=".">',
'<div class="thumb-wrap" id="{id}-{name}">',
'<div class="thumb">',
'<img src="/img/players/{picture}" title="{name} {surname}" alt="{name} {surname}" style="">',
'</div>',
'<span class="" style="height:30px;">{general_description}{name} {surname}</span>',
'</div>',
'</tpl>',
'<div class="x-clear"></div>'
],
plugins: [
Ext.create('Ext.ux.DataView.DragSelector', {}),
Ext.create('Ext.ux.DataView.LabelEditor', {dataIndex: 'name'})
],
prepareData: function(data) {
Ext.apply(data, {
name: data.name,
surname: data.surname,
general_description: Ext.util.Format.ellipsis(data.general_description, 15)
});
return data;
},
listeners: {
'selectionchange': function(record, item, index, e) {
var node = this.getNode(record); //this.getNode(record);
console.log(node.get('id'));
}
}
}) ],
dockedItems: [{
xtype: 'toolbar',
items: [{
iconCls: 'delete',
text: 'Cancella Selezionati',
scale: 'medium',
tooltip: 'Per <b>cancellare</b> i giocatori selezionati',
tooltipType: 'qtip',
id: 'delete-player',
disabled: true,
handler: delete_news
}, '-', {
iconCls: 'edit',
text: 'Aggiorna Selezionata',
scale: 'medium',
tooltip: 'Per <b>aggiornare</b> un giocatore selezionato',
tooltipType: 'qtip',
disabled: false,
id: 'update-player',
handler: function(nodes) {
var l = nodes.get('id');
console.log(l);
}
}
}
]
}]
});
Of course, this is a wrong example (because the listeners don't work) but it's just to make an idea.
There are two main things what i would like to do:
1) Catch the id (and other store's fields) of the selected item on the action "selectionchange". Obviously, now it doesn't work because of this: node.get('id'). Of course it's a wrong syntax but make up the idea of my will.
2) Catch the id of the selected item on the handler event of the "update-player" button. As above, the issue is the nodes.get('id'). Further trouble, is how to pass the selected item's features. in handler: function(nodes) { the nodes variable does not assume any value and i don't know how to pass the params from the dataview to the handler function.
I hope that somebody will able to help me.
According to the docs the selectionchange event provides the selection model as well as the array of selected records, so you are probably assuming the wrong parameters in your listener.
Without doing further testing, I think it should be something like this:
listeners: {
'selectionchange': function(selModel, selection, eOpts) {
var node = selection[0];
console.log(node.get('id'));
}
}
Note that you're using multiSelect: true, so it could be more than one record in the selection array.
Answer for second part of the question:
In button handler, you need to get selection model of the view and from it get information about selected records:
handler: function(nodes) {
// find view component
var view = dataView_player.down('dataview');
// get all selected records
var records = view.getSelectionModel().getSelection();
// process selected records
for(var i = 0; i < records.length; i++) {
console.log(records[i].getId());
}
}
Developing a Sencha Touch MVC app that pulls data from json store (thats set up to a DB pulling out content from a Wordpress Blog).
Everything works up until my "detail" panel. Instead of it listening to the TPL, its just dumping some data. The data looks similar to my blog post, but is filled with other code and doesn't make much sense.
Here is a lean version of my list:
myApp.views.PostListView = Ext.extend(Ext.Panel, {
postStore: Ext.emptyFn,
postList: Ext.emptyFn,
id:'postlistview',
layout: 'card',
initComponent: function () {
/* this.newButton = new Ext.Button({
text: 'New',
ui: 'action',
handler: this.onNewNote,
scope: this
});*/
this.topToolbar = new Ext.Toolbar({
title: 'All Posts',
/* items: [
{ xtype: 'spacer' },
this.newButton
],*/
});
this.dockedItems = [ this.topToolbar ];
this.postList = new Ext.List({
store: myApp.stores.postStore,
grouped: true,
emptyText: '<div style="margin:5px;">No notes cached.</div>',
onItemDisclosure: true,
itemTpl: '<div class="list-item-title">{title}</div>' +
'<div class="list-item-narrative"><small>{body}</small></div>',
});
this.postList.on('disclose', function (record) {
this.onViewPost(record);
}, this),
this.items = [this.postList];
myApp.views.PostListView.superclass.initComponent.call(this);
},
onViewPost: function (record) {
Ext.dispatch({
controller: myApp.controllers.masterController,
action: 'viewpost',
post: record
});
},
});
And here is the "detail" view that is called on disclosure:
myApp.views.PostSingleView = Ext.extend(Ext.Panel, {
title:'Single Post',
id:'postsingleview',
layout:'card',
style:'background:grey;',
initComponent: function () {
this.new1Button = new Ext.Button({
text: 'Back',
ui: 'back',
handler: this.onViewList,
scope: this,
dock:"left"
});
this.top1Toolbar = new Ext.Toolbar({
items: [
this.new1Button
],
title: 'Single Posts',
});
this.postSinglePanel = new Ext.Panel({
layout:'fit',
flex:1,
scroll: 'vertical',
style:'padding:10px;background:yellow;',
itemTpl: '<tpl for=".">' +
'<div class="list-item-narrative">{body}</div>' +
'</tpl>',
});
this.dockedItems = [ this.top1Toolbar, this.postSinglePanel ];
myApp.views.PostSingleView.superclass.initComponent.call(this);
},
onViewList: function () {
Ext.dispatch({
controller: myApp.controllers.masterController,
action: 'viewlist',
});
},
});
And here is the controller that its talking to:
Ext.regController('masterController', {
'index': function (options) {
if (!myApp.views.mainView) {
myApp.views.mainView = new myApp.views.MainView();
}
myApp.views.mainView.setActiveItem(
myApp.views.postView
);
},
'viewpost': function (options) {
myApp.views.postSingleView.postSinglePanel.update(options.post);
myApp.views.postView.setActiveItem(
myApp.views.postSingleView,
{ type: 'slide', direction: 'left' }
);
},
});
myApp.controllers.masterController = Ext.ControllerManager.get('masterController');
When the data comes out, it looks similar to this:
http://i.imgur.com/QlQG8.png
(the black boxes are "redacted" content, no error code there).
In closing, I believe that the controller is "dumping" the data into "MyApp.views.PostSingleView" rather than formatting it as I request in the TPL, though I'm not sure how to fix it. Any and all help MUCH appreciated!
UPDATE: As requested, here is the RegModel:
Ext.regModel("CategoryModel", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "body", type: "string"},
],
hasMany: {
model: 'Post',
name: 'posts'
}
});
And here is a sample of the json:
{
"status":"ok",
"post":{
"id":1037,
"type":"post",
"slug":"post-title",
"url":"http:\/\/localhost:8888\/jsontest\/PostTitle\/",
"status":"publish",
"title":"Post Title",
"title_plain":"Post Title",
"content":"<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br \/>\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<\/p>\n<!-- PHP 5.x -->",
"excerpt":"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat [...]",
"date":"2011-07-29 14:17:31",
"modified":"2011-08-30 01:33:20",
"categories":[
{
"id":87,
"slug":"the-category",
"title":"The Category",
"description":"",
"parent":17,
"post_count":5
}
],
"tags":[
],
"author":{
"id":2,
"slug":"tom",
"name":"tom",
"first_name":"tom",
"last_name":"",
"nickname":"",
"url":"",
"description":""
},
"comments":[
],
"attachments":[
],
"comment_count":0,
"comment_status":"open"
},
"previous_url":"http:\/\/localhost:8888\/jsontest\/next-post\/",
"next_url":"http:\/\/localhost:8888\/jsontest\/prev-post\/"
}
Use the tpl config option of the Ext.Panel not the itemTpl which doesn't exist.
As someone has mentioned before, be careful when using a Model instance and the update method, you will need to use the model's data property.
Try using this:
myApp.views.postSingleView.postSinglePanel.update(options.post.data);
the reason is that post does not actually expose the underlying data directly, you need to use the property data for that.
Also any particular reason why you are docking the postSinglePanel? I would be very careful using too many docked items as they are a known source of bugs and layout issues.
A simple way is to write your own method to update child panels (you can also see to override the default update method)
myApp.views.PostSingleView = Ext.extend(Ext.Panel, {
initComponent: function () {
// [...]
},
// [...]
myUpdate: function(data) {
this.postSinglePanel.update(data);
this.doComponentLayout(); // not sure if necessary...
}
});
and from your controller:
Ext.regController('masterController', {
// [...]
'viewpost': function (options) {
myApp.views.postSingleView.myUpdate(options.post.data); // note the .data
// [...]
},
});