Related
can we use tool-tip and annotations both in same chart in google bar chart? please share your experiences. thanks
annotations: {
textStyle: {
color: 'black',
fontSize: 11,
fontWeight: 'bold',
format: 'short',
},
alwaysOutside: true
},
tooltip: {
isHtml: true,
trigger: 'selection'
},
yes, you can use both tooltips and annotations in same chart
to do so, you use both annotation & tooltip column roles
in the data table, or data view, add the role after each data column it represents
data table
X, Y, annotation role, tooltip role
in the following example, a data view is used, so the tooltip can be built dynamically
in order to have html tooltips, two things must by in place.
the chart options must include...
tooltip: {
isHtml: true
}
and the column role must include a property...
p: {html: true}
however, there is a bug in google charts,
column properties are ignored when using a data view,
so we convert the data view to a data table when drawing...
chart.draw(view.toDataTable(), options); // <-- convert to data table
see following working snippet...
google.charts.load('current', {
packages:['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
["Element", "Density"],
["Copper", 8.94],
["Silver", 10.49],
["Gold", 19.30],
["Platinum", 21.45]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, {
type: 'string',
role: 'annotation',
sourceColumn: 1,
calc: 'stringify'
}, {
type: 'string',
role: 'tooltip',
calc: function (dt, row) {
return '<div class="ggl-tooltip"><div><span>' + dt.getValue(row, 0) + '</span></div><div>' + dt.getColumnLabel(1) + ': <span>' + dt.getValue(row, 1) + '</span></div>';
},
p: {html: true}
}]);
var options = {
annotations: {
textStyle: {
color: 'black',
fontSize: 11,
fontWeight: 'bold',
},
alwaysOutside: true
},
tooltip: {
isHtml: true,
trigger: 'selection'
}
};
var chart = new google.visualization.BarChart(document.getElementById('chart'));
chart.draw(view.toDataTable(), options);
});
.ggl-tooltip {
background-color: #ffffff;
border: 1px solid #e0e0e0;
font-family: Arial, Helvetica;
font-size: 14px;
padding: 8px;
}
.ggl-tooltip div {
margin-top: 6px;
}
.ggl-tooltip span {
font-weight: bold;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
I'm using a htmleditor field in a form and want to see the red rectangle when I use markInvalid on the field but it seems to not work.
Here a code sample :
var formPanel = Ext.create('Ext.form.Panel', {
title: 'Contact Info',
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
name: 'name',
id: 'nameId',
fieldLabel: 'Name'
}, {
xtype: 'htmleditor',
name: 'name2',
id: 'nameId2',
fieldLabel: 'Name2'
}],
bbar: [{
text: 'Mark both fields invalid',
handler: function() {
// Working fine, as expected
var nameField = formPanel.getForm().findField('name');
nameField.markInvalid('Name invalid message');
// Not working
// (but the docs specify markInvalid should works)
var nameField2 = formPanel.getForm().findField('name2');
nameField2.markInvalid('Name invalid message');
// Not working either
//var nameField3 = Ext.getCmp('nameId2');
//nameField3.markInvalid('Name invalid message');
}
}]
});
When you click on the button, only the textfield appear in red.
The HTMLeditor doesn't appear red.
Am I missing something ?
This markInvalid function is empty for htmleditor in it's source file that's why is not working.
You need to first override this htmleditor and create markInvalid like below.
Ext.define('EXT.form.field.HtmlEditor', {
override: 'Ext.form.field.HtmlEditor',
markInvalid: function(errors) {
var me = this,
oldMsg = me.getActiveError();
me.setActiveErrors(Ext.Array.from(errors));
if (oldMsg !== me.getActiveError()) {
me.updateLayout();
}
}
});
In this Fiddle, I have create a demo using HtmlEditor.
Code snippet:
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('EXT.form.field.HtmlEditor', {
override: 'Ext.form.field.HtmlEditor',
markInvalid: function (errors) {
var me = this,
oldMsg = me.getActiveError();
me.setActiveErrors(Ext.Array.from(errors));
if (oldMsg !== me.getActiveError()) {
me.updateLayout();
}
}
});
Ext.create('Ext.form.Panel', {
title: 'Contact Info',
bodyPadding: 10,
height:500,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
name: 'name',
id: 'nameId',
fieldLabel: 'Name'
}, {
xtype: 'htmleditor',
name: 'name2',
id: 'nameId2',
fieldLabel: 'Name2'
}],
bbar: [{
text: 'Mark both fields invalid',
handler: function (button) {
var form = button.up('form').getForm();
form.findField('name').markInvalid('Name invalid message');
form.findField('name2').markInvalid('Html editor invalid message');
}
}]
});
}
});
CSS
<style>
.x-form-invalid .x-html-editor-wrap {
background-color: white;
background-image: url(//cdn.sencha.com/ext/gpl/4.1.1/resources/themes/images/default/grid/invalid_line.gif);
background-repeat: repeat-x;
background-position: center bottom;
border-color: rgb(204, 51, 0);
}
</style>
I am new to ext.js and this may seem easy to some of you but not to me.
I have a form that is build with ext.js version 2.3. I am trying to place comments between the form fields. I mean the following:
inputLabel1: text form input
commentLabel: some text
inputLabel2: text form field
I am having difficulty with the second line above. I don't need it to be an input field - just a label and some text next to it, which are aligned with the form labels and input fields on the rest of the rows.
Input fields are defined as follows:
var formParts =
this.formParts = [];
var receiptAmount = new Ext.Form.NumberField( {
id: 'receiptAmount',
name: 'receiptAmount',
cls:'inputNoRightBorder ',
fieldLabel: messages.amount,
allowNegative: true,
hideLabel: false,
decimalSeparator: messages.decimalSeparator,
decimalPrecision: 2,
groupingSeparator: messages.groupingSeparator,
value: 0,
labelSeparator: messages.asteriskLineBreaker
} );
Then they are placed in containers:
var grossAmount = {
border: false,
layout: 'column',
labelWidth: 190,
cls: 'labelStyle',
border: true,
baseCls: 'ourPanel',
items: [
{
id: 'receiptAmountContainer',
border: false,
layout: 'form',
items: [receiptAmount]
},
{
id: 'receiptCurrencyContainer',
border: false,
layout: 'form',
items: [receiptCurrency]
}
]
};
The latter are placed in field sets:
var receiptFinancialData = {
id: "receiptFinancialData",
border: false,
layout: 'column',
labelWidth: 120,
cls: 'column-style',//'#background-color: #DFE8F6',
border: true,
baseCls: 'ourPanel',
height: 50,
title: messages.taxRate + ':',
header: false,
items: [
receiptExchangeRateMirror,
receiptExchangeAmountMirror,
{
border: false,
layout: 'form',
items: [grossAmount]
},
{
border: false,
layout: 'form',
cls: 'labelStyle',
items: [taxRate]
}
]
};
And again:
var singleLineItemContainer = new Ext.form.FieldSet( {
id: 'singleLineItemContainer',
name: 'singleLineItemContainer',
cls: 'singleLineItemContainer',
isActive: true,
hideLabel: true,
height: 'auto',
items: [
referenceCurrency,
receiptType,
entertainmentType,
receiptFinancialData
]
} );
And again:
var lineItemsContainer = new Ext.form.FieldSet( {
id: 'lineItemsContainer',
name: 'lineItemsContainer',
header: false,
border: false,
height: 'auto',
anchor: '100%',
items: [
toggleSingleMultyContainer,
singleLineItemContainer,
multipleLineItemsContainer,
currencyAndExchangeSet
]
} );
And again:
var generalData = new Ext.form.FieldSet( {
id: 'generalReceiptData',
name: 'generalReceiptData',
header: false,
border: false,
height: 'auto',
anchor: '100%',
items: [
receiptClass,
{
id: 'generalDataReceiptCountryAndDate',
border: false,
layout: 'column',
labelWidth: 120,
cls: 'column-style',//'#background-color: #DFE8F6',
border: true,
baseCls: 'ourPanel',
height: 50,
header: false,
items: [
{
id: 'generalDataReceiptDate',
border: false,
layout: 'form',
cls: 'dateField',
items: [receiptDate]
},
{
id: 'generalDataReceiptCountry',
border: false,
layout: 'form',
items: [receiptCountry]
}
]
},
receiptDateString,
receiptDescription,
isCompanyPaid,
lineItemsContainer
]
} );
The code above is placed in
Ext.extend( receiptForm, Ext.form.FormPanel,{
initComponent: function(){
above code
}
}
There's multiple ways to do this I'm sure. Here's a quick way to sorta hack it with css and readOnly fields
fiddle
items: [{
fieldLabel: 'inputLabel1',
}, {
fieldLabel: 'commentLabel',
readOnly:true,
style:'background: transparent;border: none;',
value:'some text',
}, {
fieldLabel: 'inputLabel2',
}],
This page has some nice ext 2.3 examples, but why not learn with a newer version?
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
})
I have a Dojo-DataGrid which is programatically populated as below :
var jsonStore = new dojo.data.ItemFileWriteStore({ url: "json/gaskets.json" });
var layout= [
{ field: "description", width: "auto", name: "Tier/Description", editable:true },
{ field: "billingMethod", width: "auto", name: "Billing Method", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "offeringComponents", width: "auto", name: "Offering Component", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "serviceActivity", width: "auto", name: "Service Activity", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "hours", width: "auto", name: "Hours" },
{ field: "rate", width: "auto", name: "Rate <br/> (EUR)" },
{ field: "cost", width: "auto", name: "Cost <br/> (EUR)" },
{ field: "price", width: "auto", name: "Price <br/> (EUR)" },
{ field: "gvn", width: "auto", name: "Gvn" }
];
grid = new dojox.grid.DataGrid({
query: { description: '*' },
store: jsonStore,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
The options for the field billingMethod (Currently defined as dojox.grid.cells.Select) are hard coded right now, but I would like to get those values dynamically from the backend as JSON. But dojox.grid.cells.Select currently(I am using Dojo 1.5) does not have a option to define a "store".
I am trying to use dijit.form.FilteringSelect, but this needs a id(of a Div) for its constructor and I cannot specify one as this select box has to go with in the grid, rather than a separate DIV.
Thanks
Sandeep
Your answer works fine, the issue is that in the combo the user can select A, but once the combo lose the focus, the value 1 will be shown. Some months ago I had the same problem, and I got a solution from KGF on #dojo. The idea is to have a formatter on the cell that just creates a SPAN element, and then it invokes a query over the store to get the label of the selected element and put it on the SPAN. I modified your example to get that working.
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer",
"billingMethod":2}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'B'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
//[kgf] callback referenced by formatter for FilteringSelect cell
function displayValue(nodeId, store, attr, item) {
if (item != null) { //if it's null, it wasn't found!
dojo.byId(nodeId).innerHTML = store.getValue(item, attr);
}
}
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
},
formatter: function(data, rowIndex) { //[kgf]
//alert("data "+data)
var genId = 'title' + rowIndex;
var store = this.widgetProps.store;
var attr = "label";
setTimeout(function() {
store.fetchItemByIdentity({
identity: data,
onItem: dojo.partial(displayValue, genId, store, attr)
});
}, 50);
//for now return a span with a predetermined id for us to populate.
return '<span id="' + genId + '"></span>';
}
}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});
I was finally able to figure this out..Incase someone wants to implement same kind of stuff using DOJO Datagrid+FilteringSelect.
Sample Code
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer"}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'Y'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
}}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});