How to use Template in rowexpander plugin - Ext JS 4 - plugins

Can we use template as follows:
plugins: [{
ptype: 'rowexpander',
selectRowOnExpand : false,
rowBodyTpl: new Ext.XTemplate(
'<p>Qusetions: {question}</p><p>',
'<tpl for="option">',
'<p>{option[0]}</p>',
'</tpl></p>'
)
}]
I am unable to see anything. I have this JSON:
{
"total": 2,
"data": [
{
"qno":1,
"question":"What's Your Fav color",
"option":['red','green','blue']
},
{
"qno":2,
"question":"What's Your coom color",
"option":['yellow','red','green','blue']
}
]
}
Model File
Ext.define('AM.model.Question', {
extend: 'Ext.data.Model',
fields: [
{name: 'question'},
{name: 'option'},
{name: 'images'},
{name: 'qno'}
]});
I want to see the output as follows:
+ Questions: What is your fav color
<radiobutton> Red
<radiobutton> Green
<radiobutton> Blue
Am using Ext JS 4.1 version
Thanks in advance for your answers

When iterating through an array using XTemplate, the current item is referred to using {.}. Your template should look something like this:
'<p>Questions: {question}</p>',
'<p>',
'<tpl for="option">',
'<p>{.}</p>',
'</tpl>',
'</p>'
However, it gets a little more tricky if you want to do a radio group. XTemplate provides the parent property to access objects outside of the current context. So instead of a <p> tag in the middle, your radio group might look like this:
'<tpl for="option">',
'<input type="radio" name="qno_{parent.qno}" value="{.}" />{.}<br/>'
'</tpl>'

Related

Flexible content modules in Silverstripe

We are looking at using Silverstripe CMS and want to be able to have modules which can be reordered.
We have come from a Wordpress setup and mostly use the flexible content ACF field. Modules (e.g. text, masthead or video) need to be able to be re-ordered.
We use our CMS's as an API so these modules are output as a section to the page or post:
[
{
"id": 10,
"title": "Post title",
"slug": "post_slug",
"path": "/post_slug",
"template": "campaign",
"published": "2017-05-25 06:09:36",
"image": null,
"seo": {
"title": "",
"description": "",
"image": {
},
},
"sections": [
{
"type": "masthead",
"Title": "",
"video": false,
"image": [
],
"showCta": false,
"cta": [
]
},
{
"type": "video_text",
"video_text": [
{
"type": "video",
"video_url": "https://www.youtube.com/watch?v=asdfa",
"video_length": "07:38",
"video_preview": false
},
{
"type": "text",
"title": "Video Title",
"content": "Video text content",
"call_to_action": false,
"cta": [
]
}
]
},
{
"type": "text",
"title": "Text Title",
"content": "",
"alignment": "centre",
"call_to_action": false,
"cta": {
"text": "CTA button",
"link_type": "internal_link",
"internal_link": "about",
"external_link": "",
"section_id": [
]
}
},
]
}
]
Does Silverstripe have it's own way of handling modules / do I need to ditch this flexible content modules method? How do others handle flexible content modules in Silverstripe?
Both silverstripe-blocks and silverstripe-elemental works very well in their own regard but I don't think they will achieve what you want. These modules don't really give you the power to use pre-defined templates. You can hook the templates in but the code will be massive. I not sure if there is an open source module for that yet.
From your JSON code, in order to have those Sections to render something like this below;
<section id="Sections">
<div id="video_text" class="section">
<iframe width="560" height="315" src="https://www.youtube.com/watch?v=asdfa" frameborder="0" allowfullscreen></iframe>
</section>
<div id="text" class="section">
<h2>Text Title</h2>
<a class='text-center btn btn-default' href="/about/">CTA button</a>
</section>
</sections>
You might want to do this.
Use DataObjects (DO) for you Sections, easy for re-ordering.
Create an abstract DO, BlockSection, with fields like Title(Varchar), Content(HTMLText), Sort(Int) and most importantly has_one to Page.
For the video use can name the DO VideoBlockSection and it extends BlockSection,
TextBlockSection for the other one. Don't forget the $singular_name for each DO (useful for nice class naming in the Grid)
On Page getCMSFields add the Grid to manage the Sections. You need to add GridFieldSortableRows and GridFieldAddNewMultiClass and now you can add you Section on each Page.
Add has_many from Page to BlockSection and a method that will render the Blocks and outputs the html.
Page.php
private static $has_many = array(
"Sections" => "BlockSection",
);
function SectionContent()
$aContent = ArrayList::create();
$oSections = $this->Sections();
if (count($oSections )) {
foreach ( $oSections as $oSection ) {
$aContent->push(ArrayData::create([
"Section" => $oSection,
"Content" => $oSection->renderWith([$oSection->ClassName, get_parent_class($oSection)]),
]));
}
}
return $aContent;
For the VideoBlockSection the template array list will be VideoBlockSection and BlockSection
VideoBlockSection.ss
<div id="video_text_{$ID}" class="section">
<iframe width="560" height="315" src="{$URL}" frameborder="0" allowfullscreen></iframe>
</section>
In you specific case, because you are using an API you need to use a wrapper to render the Template.
It needs to match [section][type] to a Template (renderWith) video_text to VideoBlockSection
Lastly in Page.ss
<% loop $SectionContent %>
{$Content}
<% end_loop %>
This was a proof of concept but its working for me so refactoring, speed and memory usage was not considered (but its working).
This way I had to ditch the unnecessary so called "page types" which I find not to be reusable in most cases.
This works 100% for me and I use it together with Bootstrap 3. I use it to create CTAs, parallax scroll, Google Map Section (multiple maps on one page), Thumbnails. Specify image resize method (Cropped, ByWidth, ByHeight).
DO NOT ditch that flexible content modules method.
I am working on an open source module which works with SS4 and Bootstrap 4 (with possibilities of using any other html framework)

Multi model data binding

OK, I have a great confusion about data binding in SAPUI5.
After reading this and this info I still don't know why my code doesn't work.
I define the data and the model in onInit function of the controller:
var oControlsData = {
controls: [
{control: [
{title: "Input Filed"},
{visible: true},
{iconUrl: "sap-icon://edit"}
]},
{control :[
{title: "Combo Box"},
{visible: true},
{iconUrl: "sap-icon://edit"}
]},
{control :[
{title: "Radio Button"},
{visible: true},
{iconUrl: "sap-icon://bo-strategy-management"}
]}
]
};
var oControlsModel = new sap.ui.model.json.JSONModel(oControlsData);
sap.ui.getCore().setModel(oControlsModel, "controls");
Then, in onAfterRendering function I try to bind data from the model to the sap.m.List control:
var oList = sap.ui.getCore().byId("controlsList");
var oListTemplate = new sap.m.StandardListItem({
title: "{title}",
visible: "{visible}",
icon: "{iconUrl}"
});
oList.bindItems({
path: "controls>/controls/control",
template: oListTemplate
});
It seems to me the path is correct, but I still get "No Data" in the control.
What went wrong here?
If I miss something big in understanding data binding process, please give a brief explanation, because every time I have to play with data binding I wonder if it work or not.
The path in bindItems should be the path to the list of Items.
So in your case controls>/controls.
The paths of the properties of the ListItem should then be relative to each object in the list.
sap.ui.getCore().byId("controlsList").bindItems({
path: "controls>/controls",
template: new sap.m.StandardListItem({
title: "{controls>control/0/title}",
visible: "{controls>control/0/visible}",
icon: "{controls>control/0/iconUrl}"
})
});
You need the control/0/ because of your inner array. If you have control over the structure of your data, I would recommend to remove that.
var oControlsData = {
controls: [
{
title: "Input Filed",
visible: true,
iconUrl: "sap-icon://edit"
},
...
]
};
On a sidenote, why are you doing databinding in onAfterRendering?
This should be done in the View. For example in XML:
<List items="{controls>/controls}">
<StandardListItem title="{controls>control/0/title}" visible="{controls>control/0/visible}" icon="{controls>control/0/iconUrl}" />
</List>

Sencha ExtJS RESTful grid example confusion

I am very confused by the Sencha documentation for ExtJS. The documentation begins with a Getting Started guide which highlights and illustrates the importance on a suitable structure for the classes and source code of your application. But the provided examples then break all the conventions laid down by the Getting Started guide. Instead of code being broken down into appropriate Model, Store, View, etc. class files the examples are provided as a single file with example source code which is not easily re-usable in separate source files.
I started by following the Portal example (http://docs.sencha.com/ext-js/4-1/#!/example/portal/portal.html) as this is the sort of application I want to create. I wanted to enhance the Portal example and add in a screen which would display a grid and use a RESTful web service as the data backend. I have created the backend I just want to create the front-end. So I looked at the RESTful example (http://docs.sencha.com/ext-js/4-1/#!/example/restful/restful.html)
I have tried to copy the RESTful example into the recommended pattern of seperate classes e.g. Model, Store, View:
Model:
Ext.define('MyLodge.model.Member', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'email', type: 'string'},
{name: 'href', type: 'string'}
]
});
Store:
Ext.require('MyLodge.model.Member');
Ext.define('MyLodge.store.Members', {
autoLoad: true,
autoSync: true,
model: 'MyLodge.model.Member',
proxy: {
type: 'rest',
url: 'http://localhost:8888/rest/memberapi/members' ,
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json'
}
},
listeners: {
write: function(store, operation){
var record = operation.getRecords()[0],
name = Ext.String.capitalize(operation.action),
verb;
if (name == 'Destroy' ) {
record = operation.records[0];
verb = 'Destroyed';
} else {
verb = name + 'd';
}
Ext.example.msg(name, Ext.String.format( "{0} member: {1}", verb, record.getId()));
}
}
});
View:
Ext.define('MyLodge.view.content.MemberGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.membergrid',
initComponent: function(){
var store = Ext.create('MyLodge.store.Members' );
Ext.apply( this, {
height: this.height,
store: store,
stripeRows: true,
columnLines: true,
columns: [{
id : 'name',
text : 'Name',
flex: 1,
sortable : true,
dataIndex: 'name'
},{
text : 'E-Mail',
width : 150,
sortable : true,
dataIndex: 'email'
},{
text : 'Href',
width : 200,
sortable : true,
dataIndex: 'href'
}],
dockedItems: [{
xtype: 'toolbar',
items: [{
text: 'Add',
iconCls: 'icon-add',
handler: function(){
// empty record
store.insert(0, new MyLodge.model.Member());
rowEditing.startEdit(0, 0);
}
}, '-', {
itemId: 'delete',
text: 'Delete',
iconCls: 'icon-delete',
disabled: true,
handler: function(){
var selection = grid.getView().getSelectionModel().getSelection()[0];
if (selection) {
store.remove(selection);
}
}
}]
}]
});
this.callParent(arguments);
}
});
But I am not sure where to put the code to control the grid row selection and enable the Delete button:
grid.getSelectionModel().on('selectionchange', function(selModel, selections){
grid.down('#delete').setDisabled(selections.length === 0);
});
Also when I press the Add button I get the following error:
Uncaught TypeError: Object [object Object] has no method 'insert'.
Any help would be appreciated.
You are having scoping issues. Basically the variable store is defined only in the initComponent function and therefore of local function scope.
Your handler function has it's own scope. It is firing in the scope of the toolbar button. So if you say this in the handler it would refer to the button. Hence you can say this.up('panel').store - and that gives you the correct reference to the store backing your grid panel.
Another advice is not to implement everything at once. Write a little bit to see if it works and then add to it little by little.
RE: the docs examples, I agree that it's frustrating, but there's not many options. Having a fully-MVC-style implementation of each example would not only be onerous to produce, but would also probably make point of the example get lost in the structure.
RE: your question about the where to "put" the code to control the grid, I would recommend setting up a controller with listeners for the events on the grid in the control() section. This will let you decouple the handling of the events that are fired by your grid from the view itself.

jquery ui autocomplete js error on keydown

i've included the jquery ui automcomplete plugin into the following structure:
<li class="search">
<input type="text" class="searchfield" name="searchfield" value="Search for Products" />
</li>
my javascript for this input field looks like:
<script type="text/javascript">
function addSearchFieldFunctionality() {
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$('.searchfield').each(function () {
$(this).autocomplete({
source: availableTags,
minLength: 1
}).data("autocomplete")._renderItem = function(ul, item) {
//console.log(item);
var a = $('<a>', {
href: item.value,
text: item.label,
"class" : "mySearchClass"
});
var b = $('<a>', {
href: item.value,
text: "Add", style: "float:right"});
var $li = $('<li></li>', {style:"width:100%"});
return $li.add(a).appendTo(ul);
};
});
}
</script>
I'm loading that function on document ready. for some reason, if a start typing e.g. the first three letters of a item, i get a resultlist. as soon as i push the keydown push button on the keyword, i get the following error in the chrome (latest version) console:
Uncaught TypeError: Cannot read property 'top' of null
a.widget.activate jquery-ui.min.js:12
a.widget.move jquery-ui.min.js:12
a.widget.next jquery-ui.min.js:12
a.widget._move jquery-ui.min.js:12
a.widget._create.element.addClass.attr.attr.bind.bind.d jquery-ui.min.js:12
f.event.dispatch jquery-1.7.1.min.js:3
f.event.add.h.handle.i
i'm using version 1.7.1 of jQuery and Version 1.8.12 of jquery UI
On the demo page of jquery ui autocomplete the keydown works well.
Any ideas what's going wrong with my constellation?
It doesn't make a difference to use remote or local data.
Best regards,
Ramo
I really can make your code working. So I tried to rewrote it in a more simplier way. The problem is render functions only accept strings, not html element. So I add a listener to render the list after its generation (fired on keydown() event).
My thought is you are doing it the wrong way.
why adding another class on those items ? they have already one, so they can be styled.
why transforming them into a nodes ? just add a click() event on them
Could you explain your functional goal ?
// Your list of links
var redirectLinks = {'Ruby': '1234.com', 'Asp': '1235.com'};
function redirect(url) {
// TODO implement window.location=url or whatever you like
if(redirectLinks[url] != undefined) {
alert('redirecting to '+url+' => '+redirectLinks[url]);
}
}
$('.searchfield').each(function () {
$(this).autocomplete(availableTags, {
minLength: 1,
change: function(event, ui) {
console.log('this change event doesnt seem to be fired');
},
select: function(event, ui) {
console.log('this select event doesnt seem to be fired');
}
});
// After the list construction
$(this).keyup(function(e){
if (e.which == 13) { // typing enter validates the input => autocompletechange
console.log('validating input '+$(this).val());
redirect($(this).val());
}
var $list = $('.ac_results:first ul').find('li');
$list.click(function() { // adding an event on suggestion => autocompleteselect
console.log('clicking on suggestion '+$(this).text());
redirect($(this).text());
});
});
});

How to select a specific node programmatically?

I have a jstree. I want to select the node which is bound to the object which has a location with id of 158. This works but seems stupid. What's the more idiomatic way of doing this?
var $tree = $('.jstree', myContext),
node = $tree.find('li').filter(function() {
return ( $(this).data().location || {}).id === 158;
});
$tree.jstree('select_node', n)
Just wanted to chime in here as none of the answers worked for me. What finally DID work was very simple:
$('#someTree').jstree('select_node', 'someNodeId');
Note that I didn't initialize someNodeId as a jQuery object. It's just a plain string.
I did this right after a tree was loaded without putting it into a "ready" bind event as it seems to not be necessary.
Hope it saves some one from a few frustrating hours. . .
To hook into the tree after it has been loaded:
.on('loaded.jstree', function() {
// Do something here...
});
Based on jsTree groups you can try
jQuery("#getStartedTree").jstree("select_node", "#test2");
if the data looks like
The JSON in the TextFile.txt - borrowed from your simple example
[
{
"data" : "A node",
"children" : [ "Child 1", "Child 2" ]
},
{
"attr" : { "id" : "test1" },
"data" : {
"title" : "Long format demo",
"attr" : { "id" : "test2", "href" : "#" }
}
}
]
and jsTree set up
My tree container is <div id="getStartedTree">
My jsTree code
$("#getStartedTree").jstree({
"themes": {
"theme": "default",
"url": "../App_Css/Themes/Default/style.css",
"dots": true,
"icons": true
},
"json_data": {
"ajax": {
"url": "../SiteMaps/TextFile.txt",
"dataType": "json",
"data": function(n) {
return { id: n.attr ? n.attr("id") : 0 };
}
}
},
"plugins": ["themes", "json_data", "ui"]
});
Is that what you are after?
I did it with:
$('.jstree').jstree(true).select_node('element id');
this code:
jQuery.each(produto.categorias, function(i, categoria) {
$('#lista-categorias').jstree(true).select_node(categoria.dadoCategoria.id);
});
I was able to simulate a click on a jstree node as an alternative way to select a node.
The following code is what was used :
$(treeIdHandle + " li[id=" + nodeId + "] a").click();
If you're populating the tree using HTML instead of JSON data and wondering how to set the node_id, you just need to set the id attribute of the <li> element!
<div class="tree-menu">
<ul class="menu">
<li id="node_1">
Node 1 - Level 1
<ul class="menu">
<li id="node_3">
Node 3 - Level 2
</li>
</ul>
</li>
<li id="node_2">
Node 2 - Level 1
</li>
</ul>
</div>
Then
$('.tree-menu')
.jstree(true)
.select_node("node_3");
will select the Node 3 - Level 2 node.
For those getting javascript errors, remember to use Full version of jQuery, not the slim version!
For all down voters, here is the demo to prove it's working:
https://jsfiddle.net/davidliang2008/75v3fLbs/7/
i use jstree 3.0.8. don't use 'state'
'plugins' : ['dnd','sort','types','contextmenu','wholerow','ui']
and server offer the json, the selected node has
"state":{"selected":true,"opened":true}
This solution Works for me
// after the tree is loaded
$(".jstree").on("loaded.jstree", function(){
// don't use "#" for ID
$('.jstree').jstree(true).select_node('ElementId');
});
and even in a php loop (dynamically) :
$(".jstree").on("loaded.jstree", function(){
<?php foreach($tree as $node): ?>
$('.jstree').jstree(true).select_node('<?=$node?>');
<?php endforeach;?>
});
Hope this works for you.
i think u should write code to select node after jstree initialized, therefore use this code
$('#jstree')
.on('ready.jstree', function (e, data) {
// do function after jstree initialized
$('#jstree')
.jstree(true)
.select_node('nodeid');
});
hope its work :)
trigger click on first anchor
$("#jstree .jstree-anchor:first").click();
or by node id 158
$("#jstree #158").find(".jstree-anchor:first").click();
$('#' + 158).find(".jstree-anchor:first").click();