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

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
})

Related

ExtJs getRecord() function of form does not work

I would like to get my values from my form through the getRecord() function.
This is the result im currently getting from form.getRecord():
The data object is empty.
This is my controller:
onAddNewsClick: function(button, e, eOpts) {
var win = this.getNewsEdit();
if(!win){
win = Ext.create('widget.newsedit');
}
this.getNewsPanel().loadRecord(Ext.create('model.news'));
console.log(this.getNewsPanel());
this.adding = true;
win.show();
},
OnSaveNewsClick: function(button, e, eOpts) {
var form = this.getNewsPanel();
console.log(form);
var selectedRecord = form.getRecord();
console.log(selectedRecord);
if (this.adding) {
this.adding = undefined;
}
}
The correspoding Model:
Ext.define('mobile_admin.model.News', {
extend: 'Ext.data.Model',
alias: 'model.news',
requires: [
'Ext.data.field.Field'
],
fields: [
{
name: 'title'
},
{
name: 'newscontent'
},
{
name: 'newsdate'
},
{
name: 'status'
}
]});
With form.getValues() I get all the values of the form.
It feels like that the model is not connecting to the form. In the form I put the necessary name connection: (http://docs.sencha.com/extjs/5.1.0/Ext.form.Basic.html#method-loadRecord)
My Form items:
items: [
{
xtype: 'textfield',
fieldLabel: 'Titel',
name: 'title'
},
{
xtype: 'datefield',
fieldLabel: 'Datum',
name: 'newsdate'
},
{
xtype: 'htmleditor',
height: 150,
width: 600,
fieldLabel: 'Inhalt',
name: 'newscontent',
},
{
xtype: 'textfield',
fieldLabel: 'Status',
name: 'status'
},
]
Anyone has an idea?
I use ExtJs (5.1.3).
//EDIT:
I found out that I can set the record like this:
var b = form.getRecord()
b.set(form.getValues()
Shouln't this ExtJs do automatically?
What loadRecord really do is just setting values. Form don't use this record any more. Here is form docs:
loadRecord: function(record) {
this._record = record;
return this.setValues(record.getData());
},
You asked if setting values to record can be done automatically. Yes, it is possible, but binding is required.
I prepared a sample code:
Ext.define('mobile_admin.model.News', {
extend: 'Ext.data.Model',
alias: 'model.news',
requires: [
'Ext.data.field.Field'
],
fields: [
{
name: 'title', defaultValue : 'test'
},
{
name: 'newscontent'
},
{
name: 'newsdate'
},
{
name: 'status'
}
]
});
Ext.define('mobile_admin.form.ViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.formpanel',
links : {
theModel : {
type : 'mobile_admin.model.News',
create : true
}
}
});
Ext.define('mobile_admin.form.formpanel', {
extend: 'Ext.form.Panel',
alias: 'widget.newsedit',
viewModel : 'formpanel',
items : [
{
xtype: 'textfield',
fieldLabel: 'Titel',
name: 'title',
bind : {
value : '{theModel.title}'
}
},
{
xtype: 'datefield',
fieldLabel: 'Datum',
name: 'newsdate',
bind : {
value : '{theModel.newsdate}'
}
},
{
xtype: 'htmleditor',
height: 150,
width: 600,
fieldLabel: 'Inhalt',
name: 'newscontent',
bind : {
value : '{theModel.newscontent}'
}
},
{
xtype: 'textfield',
fieldLabel: 'Status',
name: 'status',
bind : {
value : '{theModel.status}'
}
}],
buttons : [{
text : 'Save',
handler : function(button) {
var vM = button.up('form').getViewModel();
vM.notify();
console.log(vM.get('theModel').getData());
}
}]
});
Ext.application({
name : 'Fiddle',
launch : function() {
debugger;
var form = Ext.widget('newsedit');
var window = Ext.widget('window', {
items : form
});
window.show();
}
});

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.

Submit all of grid rows with Extjs form submit

I have a grid panel in a form. Any row of the grid panel have a filefield. I want to send any row (name field and filename field) to server.
Model:
Ext.define('FM.model.DefineCode', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'filename', type: 'string'}
],
validations: [
{type: 'presence', field: 'id'},
{type: 'presence', field: 'name'},
{type: 'presence', field: 'filename'}
]
});
Store:
Ext.define('FM.store.DefineCode', {
extend: 'Ext.data.Store',
model: 'FM.model.DefineCode',
autoLoad: true,
data: []
});
View:
Ext.define('FM.view.map.DefineCode', {
extend: 'Ext.window.Window',
title: 'Define Code',
alias: 'widget.mmDefineCode',
width: 600,
modal: true,
items: [{
xtype: 'form',
items: [{
xtype: 'gridpanel',
title: 'myGrid',
store: 'DefineCode',
columns: [
{
text: 'Id',
xtype: 'rownumberer',
width: 20
}, {
text: 'Name',
dataIndex: 'name',
flex: 1,
editor:{
xtype: 'textfield'
}
}, {
text: 'File',
dataIndex: 'filename',
width: 200,
editor:{
xtype: 'filefield',
emptyText: 'Select your Icon',
name: 'photo-path',
buttonText: '',
flex: 1,
buttonConfig: {
iconCls: 'icon-upload-18x18'
},
listeners: {
change: function(e, ee, eee) {
var grid = this.up('grid');
var store = grid.getStore();
var newStore = Ext.create('FM.store.DefineCode',{});
store.insert(store.data.items.length, newStore);
}
}
},
}, {
text: '',
width: 40
}
],
height: 200,
width: 600,
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
]}
],
}],
buttons: [{text: 'OK', action: 'OK'}],
initComponent: function() {
var me = this;
Ext.apply(me, {});
me.callParent(arguments);
}
});
Controller:
...
form.submit({
url: 'icon/create',
});
When I submit form, only last row is sending to server.
Where is problem?
Why you using this ?
var newStore = Ext.create('FM.store.Path', {});
store.insert(store.data.items.length, newStore);
try using rowEditing to edit/submit 1 row data :
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToEdit: 2,
clicksToMoveEditor: 1,
listeners: {
'validateedit': function(editor, e) {},
'afteredit': function(editor, e) {
var me = this;
var jsonData = Ext.encode(e.record.data);
Ext.Ajax.request({
method: 'POST',
url: 'your_url/save',
params: {data: jsonData},
success: function(response){
e.store.reload({
callback: function(){
var newRecordIndex = e.store.findBy(
function(record, filename) {
if (record.get('filename') === e.record.data.filename) {
return true;
}
return false;
}
);
me.grid.getSelectionModel().select(newRecordIndex);
}
});
}
});
return true;
}
}
});
and place it to your plugin.
I don't try it first, but may be this will help you a little.
AND this is for your controller to add a record from your grid, you need a button to trigger this function.
createRecord: function() {
var model = Ext.ModelMgr.getModel('FM.model.DefineCode');
var r = Ext.ModelManager.create({
id: '',
name: '',
filename: ''
}, model);
this.getYourgrid().getStore().insert(0, r);
this.getYourgrid().rowEditing.startEdit(0, 0);
}
check this for your need, this is look a like. You need to specify the content-type.
And for your java need, please read this method to upload a file.

SAPUI5 - property scope

I have two controls, one that extends from Control, and one that extends from my custom control.
The controls have an array property that I want to be unique per instance:
/* parent control */
sap.ui.core.Control.extend("foo.MyControl", {
metadata: {
properties: {
name: { type: 'string' },
myArray: { type: 'array', defaultValue: [] }
}
},
renderer: {
render: function( oRenderManager, oControl ) {
oRenderManager.write('<div>My name is: ' + oControl.getName() + '</div>');
}
}
});
/* child control */
foo.MyControl.extend("foo.MyChildControl", {
renderer: {
render: foo.MyControl.prototype.render
}
});
And then I create instances of them and place them in the dom:
/* create and place instances */
var oMyControl = new foo.MyControl({
name: 'parent control'
}),
oMyChildControl = new foo.MyChildControl({
name: 'child control'
});
oMyControl.placeAt('uiArea');
oMyChildControl.placeAt('uiArea');
Why are the 2 array instances equal?
alert(oMyControl.getMyArray() === oMyChildControl.getMyArray()); // true
I would expect that each instance would get it's own array.
**
TL;DR For defining control metadata, declare array properties as type "object"
**
i got exceptions when trying to run your code
type: 'array' isn't correct
The typeof operator in JavaScript returns "object" for arrays.
this works for me
sap.ui.core.Control.extend("foo.MyControl", {
metadata: {
properties: {
name: {
type: 'string'
},
myArray: {
type: 'object',
defaultValue: []
}
}
},
renderer: {
render: function(oRenderManager, oControl) {
oRenderManager.write('<div>' + oControl.getMyArray().toString() + '</div>');
}
}
});
/* child control */
foo.MyControl.extend("foo.MyChildControl", {
renderer: {
render: foo.MyControl.prototype.render
}
});
/* create and place instances */
var oMyControl = new foo.MyControl({
name: 'parent control'
}).placeAt('content');
var oMyChildControl = new foo.MyChildControl({
name: 'child control'
}).placeAt('content');
var aFruits = ["Banana", "Orange", "Apple", "Mango"];
var aColors = ["Red", "Blue", "Green"];
oMyControl.setMyArray(aFruits);
oMyChildControl.setMyArray(aColors);

ExtJs - Render object 'layout form' dynamically in DOM

I have a function :
var my_form = function() {
return {
layout:'form',
items: [
{
xtype:'textfield',
fieldLabel: "Name",
maxLength: 255
}
]
};
}
I want to render it dynamically. This doesn't work :
var t = Ext.DomHelper.createTemplate(my_form()).compile();
Ext.DomHelper.insertBefore('my_div', t);
How to do this ?
Thanks :)
I found out it can work this way: first create a div via DomHelper, then create the FormPanel using the "renderTo" config option.
Ext.onReady(function(){
var formdiv = Ext.DomHelper.insertBefore('my_div', {tag: 'div', id: 'form_div' } );
var my_form = function() {
return {
layout:'form',
renderTo:'form_div',
items: [
{
xtype:'textfield',
fieldLabel: "Name",
maxLength: 255
}
]
};
}
var t = new Ext.FormPanel(my_form());
});