Ext TreePanel: How to add Child Nodes to the expanded node? - extjs4.2

I am trying to create a Tree from Ext.tree.Panel with JSON data.
My JSON
{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}
I want the first level nodes to be the "firstName: from the above JSON. I am able to achieve this but when any of the node is expanded the same nodes are displayed as children to the expanded node shown below.
- John
---> + John
---> + Anna
---> + Peter
+ Anna
+ Peter
When any of the node is expanded I want to add children from another JSON file. but here the problem is before adding the children to the node i can see the child nodes attached as above.
Is there any way to avoid this behavior ?
Code Snippet
My TreePanel:
Ext.define('MyApp.view.MyTreePanel', {
extend: 'Ext.tree.Panel',
height: 439,
width: 400,
title: 'My Tree Panel',
store: 'MyTreeStore',
displayField: 'firstName',
rootVisible: false,
initComponent: function() {
var me = this;
Ext.applyIf(me, {
viewConfig: {
}
});
me.callParent(arguments);
}
});
TreeStore:
Ext.define('MyApp.store.MyTreeStore', {
extend: 'Ext.data.TreeStore',
requires: [
'MyApp.model.MyModel'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
autoSync: true,
model: 'MyApp.model.MyModel',
storeId: 'MyTreeStore',
proxy: {
type: 'ajax',
url: 'employees.json',
reader: {
type: 'json',
root: 'employees'
}
}
}, cfg)]);
}
});
**Model:**
Ext.define('MyApp.model.MyModel', {
extend: 'Ext.data.Model',
fields: [
{
name: 'firstName'
}
]
});
Thanks in advance....

The url will get called for every node you click on that hasn't been 'loaded' yet. I'm not sure why ExtJS does this as the parameters change per node clicked. What I've done because I needed a lot of extra functionality is override the load() in the tree.Store (sorry, it's a little messy and could use a cleanup):
Ext.define('MyApp.store.MyTreeStore', {
extend: 'Ext.data.TreeStore',
load: function(options) {
options = options || {};
options.params = options.params || {};
var me = this,
node = options.node || me.tree.getRootNode(),
root;
// If there is not a node it means the user hasnt
// defined a rootnode yet. In this case lets just
// create one for them.
if (!node) {
node = me.setRootNode({
expanded: true
});
}
if (me.clearOnLoad) {
node.removeAll();
}
Ext.applyIf(options, {
node: node
});
options.params[me.nodeParam] = node ? node.getId() : 'root';
if (node) {
node.set('loading', true);
}
var root = me.getRootNode();
if (node.isRoot()) {
// put some root option.params here.
// otherwise, don't pass any if you don't need to
} else {
options.params = Ext.JSON.encode({
// or some parameter here for the node to
// get back the correct JSON data
nodeId: node.get('id')
});
}
return me.callParent([options]);
},
model: 'MyApp.model.MyModel',
storeId: 'MyTreeStore',
proxy: {
type: 'ajax',
url: 'employees.json',
reader: {
type: 'json',
root: 'employees'
}
},
autoLoad: false,
listeners: {
append: function(store,node,index) {
// on append depth 1 is first level, beforeappend it is 0
if (node.get('depth') == 1) {
// setting these values here assures that the node knows it's been loaded already.
// node.set('leaf',true); force leaf true, false or whatever you want.
node.set('loaded',true);
node.commit();
}
}
}
});
Also, you can do something like this to your JSON if you need to have the expand / collapse icons there. These are default fields that get added by default.
{
"employees": [
{ "firstName":"John" , "lastName":"Doe", leaf: false, children: []},
{ "firstName":"Anna" , "lastName":"Smith", leaf: false, children: []},
{ "firstName":"Peter" , "lastName":"Jones", leaf: false, children: []}
]
}

Related

Angular-datatables server-side processing and Buttons extension. data is empty

use Angular4 and angular-datatables.
Everything looks fine (pagination, column sorting, export buttons), but when I try to print/copy/export a table, it only outputs the headers, even though the rest of the data is there.
Any ideas what's should I check?
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 20,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
this.service.getServerSidePaganated(dataTablesParameters).subscribe(resp => {
that.cdrs = resp.data;
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [
{ data: 'source' },
{ data: 'destination' },
{ data: 'dateTime'},
{ data: 'callTime'},
{ data: 'timeOnHold'},
{ data: 'disposition'},
{ data: 'recordingFileName'}
],
// Declare the use of the extension in the dom parameter
dom: 'Bfrtip',
// Configure the buttons
buttons: [
'print',
'excel'
]
};
}
you should set the callback data
ajax: (dataTablesParameters: any, callback) => {
this.service.getServerSidePaganated(dataTablesParameters).subscribe(resp => {
// that.cdrs = resp.data; // remove this line
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: resp.data // set data
});
});

How can I filter a TreeStore in ExtJS 6.2?

I need to filter a Tree (search for a node by comparing a text to a node attribute).
This fiddle is exactly what I need, but seems it is not working with ExtJS 6.2.
After a day trying to find out what is wrong in my code, I've decided to simply change the framework version in the fiddle from 4.2.1 (default) to 6.2.981 classic gray and can see the code is obsolete (removing all nodes or an arbitrary amount I can't see why).
Can someone verify that code and tell me how to port it to v6.2?
EDIT:
Almost there. This code is doing the job, but stops in deep level 2, not all the tree: JSFiddle
Ext.define('TreeFilter', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.treefilter',
collapseOnClear: true, // collapse all nodes when clearing/resetting the filter
allowParentFolders: false, // allow nodes not designated as 'leaf' (and their child items) to be matched by the filter
init: function(tree) {
var me = this;
me.tree = tree;
tree.filter = Ext.Function.bind(me.filter, me);
tree.clearFilter = Ext.Function.bind(me.clearFilter, me);
},
filter: function(value, property, re) {
var me = this,
tree = me.tree,
matches = [], // array of nodes matching the search criteria
root = tree.getRootNode(), // root node of the tree
property = property || 'text', // property is optional - will be set to the 'text' propert of the treeStore record by default
re = re || new RegExp(value, "ig"), // the regExp could be modified to allow for case-sensitive, starts with, etc.
visibleNodes = [], // array of nodes matching the search criteria + each parent non-leaf node up to root
viewNode;
if (Ext.isEmpty(value)) { // if the search field is empty
me.clearFilter();
return;
}
tree.expandAll(); // expand all nodes for the the following iterative routines
// iterate over all nodes in the tree in order to evalute them against the search criteria
root.cascadeBy(function(node) {
if (node.get(property).match(re)) { // if the node matches the search criteria and is a leaf (could be modified to searh non-leaf nodes)
matches.push(node) // add the node to the matches array
}
});
if (me.allowParentFolders === false) { // if me.allowParentFolders is false (default) then remove any non-leaf nodes from the regex match
Ext.each(matches, function(match) {
if (!match.isLeaf()) {
Ext.Array.remove(matches, match);
}
});
}
Ext.each(matches, function(item, i, arr) { // loop through all matching leaf nodes
root.cascadeBy(function(node) { // find each parent node containing the node from the matches array
if (node.contains(item) == true) {
visibleNodes.push(node) // if it's an ancestor of the evaluated node add it to the visibleNodes array
}
});
if (me.allowParentFolders === true && !item.isLeaf()) { // if me.allowParentFolders is true and the item is a non-leaf item
item.cascadeBy(function(node) { // iterate over its children and set them as visible
visibleNodes.push(node)
});
}
visibleNodes.push(item) // also add the evaluated node itself to the visibleNodes array
});
root.cascadeBy(function(node) { // finally loop to hide/show each node
viewNode = Ext.fly(tree.getView().getNode(node)); // get the dom element assocaited with each node
if (viewNode) { // the first one is undefined ? escape it with a conditional
viewNode.setVisibilityMode(Ext.Element.DISPLAY); // set the visibility mode of the dom node to display (vs offsets)
viewNode.setVisible(Ext.Array.contains(visibleNodes, node));
}
});
}
,
clearFilter: function() {
var me = this,
tree = this.tree,
root = tree.getRootNode();
if (me.collapseOnClear) {
tree.collapseAll();
} // collapse the tree nodes
root.cascadeBy(function(node) { // final loop to hide/show each node
viewNode = Ext.fly(tree.getView().getNode(node)); // get the dom element assocaited with each node
if (viewNode) { // the first one is undefined ? escape it with a conditional and show all nodes
viewNode.show();
}
});
}
});
// EXAMPLE
var store = Ext.create('Ext.data.TreeStore', {
root: {
expanded: true,
children: [{
text: "detention",
leaf: true
}, {
text: "homework",
expanded: false,
children: [{
text: "book report",
leaf: true
}, {
text: "algebra",
leaf: true
}]
}, {
text: "chores",
expanded: false,
children: [{
text: "do homework",
leaf: true
}, {
text: "walk dog",
leaf: true
}, {
text: "clean room",
leaf: true
}, {
text: "wash dishes",
leaf: true
}, {
text: "laundry",
leaf: true
}]
}, {
text: "buy lottery tickets",
leaf: true
}, {
text: "take over world",
leaf: true
}, {
text: "Sencha",
expanded: false,
children: [{
text: "Touch",
expanded: false,
children: [{
text: 'Viewport',
leaf: true
}, {
text: 'Panel',
leaf: true
}, {
text: 'Carousel',
leaf: true
}]
}, {
text: "ExtJS",
expanded: false,
children: [{
text: 'viewport.Viewport',
leaf: true
}, {
text: 'panel.Panel',
leaf: true
}, {
text: 'tree.Panel',
leaf: true
}]
}]
}]
}
});
Ext.create('Ext.tree.Panel', {
title: 'Simple Tree',
width: 200,
height: 150,
store: store,
rootVisible: false,
renderTo: Ext.getBody(),
plugins: [{
ptype: 'treefilter',
allowParentFolders: true
}],
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
xtype: 'trigger',
triggerCls: 'x-form-clear-trigger',
onTriggerClick: function() {
this.reset();
this.focus();
},
listeners: {
change: function(field, newVal) {
var tree = field.up('treepanel');
tree.filter(newVal);
},
buffer: 250
}
}]
}]
});
Without using filter plugin i have added some useful code in change event and it works for all depth.
"dockedItems": [{
xtype: 'toolbar',
dock: 'top',
items: [{
xtype: 'textfield', //As of Ext JS 5.0 trigger class has been deprecated. It is recommended to use {#link Ext.form.field.Text Text Field}.
triggerCls: 'x-form-clear-trigger',
width: 300,
onTriggerClick: function () {
this.reset();
this.focus();
},
listeners: {
change: function (field, newVal) {
var reportBuilderStore = field.up('panel').getStore();
if (!Ext.isEmpty(field.value)) {
reportBuilderStore.filterBy(function (rec) {
var childs = !Ext.isEmpty(rec.get('children')) ? rec.get('children').map(function (x) {
return x.text;
}) : [];
var matched = false;
for (var val of childs) {
if (val.toUpperCase().match((field.value).toUpperCase())) {
matched = true;
break;
}
}
if (!Ext.isEmpty(rec.get('text').toUpperCase().match((field.value).toUpperCase())) || rec.get('text').toUpperCase() == "ROOT" || matched)
return true;
});
} else {
reportBuilderStore.clearFilter();
}
},
buffer: 250
}
}
]
}
],

Get slug from object in view won't work

I have created a new object named Project, that contains a gallery and some other fields in it. In the view, I'm showing some data from it and I want to put a link to previous and next project. I already managed to get the previous project but when I try to get the slug from it, somehow it doesn't work.
This is the Project model:
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* Project Model
* ==========
*/
var Project = new keystone.List('Project', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
Project.add({
title: { type: String, required: true },
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft', index: true },
author: { type: Types.Relationship, ref: 'User', index: true },
publishedDate: { type: Types.Date, index: true, dependsOn: { state: 'published' } },
category: { type: String, required: false },
description: { type: Types.Html, wysiwyg: true, height: 150 },
shortDescription: { type: Types.Html, wysiwyg: true, height: 100 },
credits: { type: Types.Html, wysiwyg: true, height: 100 },
galleries: { type: Types.Relationship, ref: 'Gallery', many: false },
color: { type: String, required: false }
});
Project.schema.virtual('content.full').get(function() {
return this.content.extended || this.content.brief;
});
Project.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Project.register();
This is the controller:
var keystone = require('keystone');
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// Set locals
locals.section = 'projects';
locals.filters = {
project: req.params.project
};
locals.data = {
projects: [],
previousProject: []
};
// Load the current project
view.on('init', function(next) {
var q = keystone.list('Project').model.findOne({
state: 'published',
slug: locals.filters.project
}).populate('galleries');
q.exec(function(err, result) {
locals.data.project = result;
next(err);
});
});
//Load other projects
view.on('init', function(next) {
var q = keystone.list('Project').model.find({state: "published", publishedDate: {$lt: locals.data.project.publishedDate}}).sort('-publishedDate').limit(1);
q.exec(function(err, results) {
locals.data.previousProject = results;
next(err);
});
});
// Render the view
view.render('project');
};
And this is the view:
<div class="container">
<p>{{{data.project.title}}}</p>
<p>—</p>
<p>{{{data.project.category}}}</p>
{{#if data.project.galleries}}
{{#each data.project.galleries.images}}
<img src="{{url}}" />
{{/each}}
{{/if}}
<p>full project: {{data.previousProject}}</p>
<p>slug: {{data.previousProject.slug}}</p>
{{#if data.previousProject}}
<a href="/projects/{{data.previousProject.slug}}" >Previous project</a>
{{/if}}
</div>
Somehow, {{data.previousProject}} shows the correct record info but when I do {{data.previousProject.slug}} it returns nothing at all. I've been scratching my head against this for hours but I can't find where is the issue. Thanks in advance!!
I finally found what the issue was: in the controller I'm using model.find whereas I should be using model.findOne if I know I only need one record and I want to directly get the values from it with .slug. Using limit(1) was not enough.

How to build a quantity plus / minus composite control?

I try to build an input field for quantities with plus and minus buttons. More like to understand how composite controls are working. I found this documentation.
I thought I could use the new control like this:
new ex.perimental.Input({
width: "14em",
editable: true,
input: new sap.m.Input({
width: "8em",
value: {
path: "model>Quantity",
type: new sap.ui.model.type.Integer({}, {
minimum:0
})
},
description: "{model>Unit}"
}),
})
The control code looks like this:
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_increaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
_decreaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
input: { type: "sap.m.Input", multiple: false },
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
oInput.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
oInput.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this.setAggregation(
"_decreaseButton",
new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
})
);
this.setAggregation(
"_increaseButton",
new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
})
);
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this.getAggregation("_decreaseButton"),
this.getAggregation("_increaseButton"),
],
})
);
},
setEditable: function(sValue) {
debugger;
// aggregations will be null now
// I assume because they are reused in the HBox control
// this.getAggregation("_increaseButton").setEditable(sValue);
// this.getAggregation("_decreaseButton").setEditable(sValue);
// this.getAggregation("input").setEditable(sValue);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAggregation("input", oInput);
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
It will be rendered but the setEditable is not working.
The buttons (used inside the HBox control again) are not reachable via getAggregation. Also the input field (set from outside) can't be accessed.
Not sure how to do it right. Anyone an idea?
Edit2
This is the latest version but still not working.
I am asking me how to put the externally defined input control into the right place inside the inner Hbox control and be able to access this control in methods like setEditable?
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
associations: {
input: { type: "sap.m.Input", multiple: false, singularName: "input" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
this._input.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
this._input.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this._decreaseButton = new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
});
this._increaseButton = new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
});
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this._decreaseButton,
this.getAssociation("input"),
this._increaseButton,
],
})
);
},
setEditable: function(sValue) {
var bEditable = false;
if (sValue === true) {
bEditable = true;
}
this._decreaseButton.setEnabled(bEditable);
this._increaseButton.setEnabled(bEditable);
// seems not always working
this._input.setEditable(bEditable);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAssociation("input", oInput);
this._input = oInput;
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
I still have problems with the association handling (updated code) I guess handling the association should be done different? Sometimes the input field is still null.
a control can only be aggregated by one control at a time.
This is the difference between associations (control may be at multiple at the same time) and aggregations.
What you can do in your init is:
this._decreaseButton = new Button (...)
Basically you only need one aggregation for your HBox.
If your buttons are then aggregated by the HBox they will know the models of the parent and also be destroyed.
The only thing you need to check if your Root control is a Ancestor of the created controls (use myControl.getParent()).
best regards,
Tobias

How to dynamically create form.Fields from Stores in ExtJS 4

I have two stores, Assessor.store.Question and Assessor.store.Choice, along with their respective Models and Proxies. The load data from the server as intended. I also have a Panel, Assessor.view.QuizCards, with a "card" layout. This works fine and I can create dummy cards, Assessor.view.QuestionCard, and navigate through them fine using the Assessor.controller.Quiz controller.
What I need help with is programatically populating my QuizCards panel with questions and choices from the Questions and Choices stores. I've tried just about everything I can think of based on the docs and have had absolutely no success.
Specifically, I want the "value" of the "displayfield" on a QuestionCard to be the "text" property from the Question store/model. The "boxlabel" values in the "radiogroup" should come from the associated Choice store/model.
The detailed code is below. Thanks for any guidance you can provide.
Ext.define('Assessor.controller.Quiz', {
extend: 'Ext.app.Controller',
itemId: 'quizcontroller',
models: ['Question', 'Choice'],
stores: ['Question', 'Choice'],
views: ['QuestionCard'],
// Constants, kinda
NUM_QUESTIONS: 4,
// Custom Functions
/**
* create store instances
*/
createStores: function(){
if (Ext.getStore('questionstore') == null) {
var qs = Ext.create('Assessor.store.Question');
qs.load();
};
if (Ext.getStore('choicestore') == null) {
var cs = Ext.create('Assessor.store.Choice');
cs.load();
};
}, //end createStores
/**
* update buttons
*/
updateButtons: function(){
var index = this.getCardIndex();
var nb = Ext.ComponentQuery.query('#nextbutton')[0];
var pb = Ext.ComponentQuery.query('#prevbutton')[0];
var fb = Ext.ComponentQuery.query('#finishbutton')[0];
if (index<this.NUM_QUESTIONS) {
nb.enable();
fb.disable();
} else {
nb.disable();
fb.enable();
};
if (index>0){
pb.enable();
} else {
pb.disable();
};
}, //end updateButtons
/**
* get active question card index
*/
getCardIndex: function(){
return (Ext.ComponentQuery.query('quizcards')[0].getLayout().activeItem.itemId.split('-')[1]);
},
/**
* set active question card index
*/
setCardIndex: function(index){
Ext.ComponentQuery.query('quizcards')[0].getLayout().setActiveItem('questioncard-'+index);
},
/**
* start the quiz
*/
startQuiz: function(args) {
this.createQuestionCards();
var sb = Ext.ComponentQuery.query('#startbutton')[0];
sb.disable();
this.updateButtons();
},
/**
* create the UI cards with questions from server.
*/
createQuestionCards: function() {
var qc = Ext.ComponentQuery.query('quizcards')[0];
for (i=0; i<this.NUM_QUESTIONS; i++) {
card = Ext.create('Assessor.view.QuestionCard');
card.itemId = 'questioncard-' + i.toString();
qc.add(card);
};
this.updateButtons();
},
/**
* finishQuiz -- finishes and scores the quiz
* #param {Object} args
*/
finishQuiz: function(args) {
this.localState.set('quizFinished', true);
},
//
nextQuestion: function(args) {
console.log('\nnextQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex < this.NUM_QUESTIONS) {
activeIndex++;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
prevQuestion: function(args) {
console.log('\nprevQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex > 0) {
activeIndex--;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
init: function(){
this.control({
'#nextbutton': {
click: this.nextQuestion
},
'#prevbutton': {
click: this.prevQuestion
},
'#startbutton': {
click: this.startQuiz
},
'#finishbutton': {
click: this.finishQuiz
},
})
}
})
Ext.define('Assessor.view.QuizCards', {
extend: 'Ext.panel.Panel',
alias: 'widget.quizcards',
itemId: 'quizcards',
layout: 'card',
activeItem: 0,
items: []
})
Ext.define('Assessor.view.QuestionCard', {
extend: 'Ext.form.Panel',
alias: 'widget.questioncard',
layout: 'anchor',
items: [{
xtype: 'displayfield',
itemId: 'questionfield',
name: 'questionfield',
fieldLabel: 'Question',
value: ''
}, {
xtype: 'radiogroup',
itemId: 'choicegroup',
columns: 1,
vertical: true,
items: [{
boxLabel: '',
name: 'choice',
value: 1
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 2
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 3
}]
}]
})
Ext.define('Assessor.store.Question', {
extend: 'Ext.data.Store',
autoLoad: true,
autoSync: true,
model: 'Assessor.model.Question',
storeId: 'questionstore'
})
Ext.define('Assessor.model.Question', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'text', type: 'string'},
{name: 'resource_uri', type: 'string'}
],
proxy: {
type: 'rest',
url: '/api/v1/question/',
headers: {
'accept':'application/json',
'content-type':'application/json'
},
noCache: false,
reader: {
type: 'json',
root: 'objects',
idAttribute: 'id'
},
writer: {
type: 'json'
}
}
})
Choice model and store are similar, I'll post them if needed. Thanks
What I would suggest for the start is to make Assessor.view.QuestionCard more smart. I would rewrite initComponent there and pass record from the store during construction. This way you would have all logic for creating specific UI elements inside Assessor.view.QuestionCard and would call it something like that:
card = Ext.create('Assessor.view.QuestionCard', {
questionRecord: rec,
lastQuestion: true/false...
... whatever else you need
})