PDA

View Full Version : DataTip - show a complex tooltip based upon node's attributes or Record's data.



Animal
10 Mar 2010, 12:45 AM
/**
* @class Ext.ux.DataTip
* @extends Ext.ToolTip.
* <p>This plugin implements automatic tooltip generation for an arbitrary number of child nodes <i>within</i> a Component.</p>
* <p>This plugin is applied to a high level Component, which contains repeating elements, and depending on the host Component type,
* it automatically selects a {@link Ext.ToolTip#delegate delegate} so that it appears when the mouse enters a sub-element.</p>
* <p>When applied to a GridPanel, this ToolTip appears when over a row, and the Record's data is applied
* using this object's {@link Ext.Component#tpl tpl} template.</p>
* <p>When applied to a DataView, this ToolTip appears when over a view node, and the Record's data is applied
* using this object's {@link Ext.Component#tpl tpl} template.</p>
* <p>When applied to a TreePanel, this ToolTip appears when over a tree node, and the Node's {@link Ext.tree.TreeNode#attributes attributes} are applied
* using this object's {@link Ext.Component#tpl tpl} template.</p>
* <p>When applied to a FormPanel, this ToolTip appears when over a Field, and the Field's <code>tooltip</code> property is used is applied
* using this object's {@link Ext.Component#tpl tpl} template, or if it is a string, used as HTML content.</p>
* <p>If more complex logic is needed to determine content, then the {@link Ext.Component#beforeshow beforeshow} event may be used.<p>
* <p>This class also publishes a <b><code>beforeshowtip</code></b> event through its host Component. The <i>host Component</i> fires the
* <b><code>beforeshowtip</code></b> event.
*/
Ext.ux.DataTip = Ext.extend(Ext.ToolTip, (function() {

// Target the body (if the host is a Panel), or, if there is no body, the main Element.
function onHostRender() {
var e = this.body || this.el;
if (this.dataTip.renderToTarget) {
this.dataTip.render(e);
}
this.dataTip.initTarget(e);
}

function updateTip(tip, data) {
if (tip.rendered) {
tip.update(data);
} else {
if (Ext.isString(data)) {
tip.html = data;
} else {
tip.data = data;
}
}
}

function beforeTreeTipShow(tip) {
var e = Ext.fly(tip.triggerElement).findParent('div.x-tree-node-el', null, true),
node = e ? tip.host.getNodeById(e.getAttribute('tree-node-id', 'ext')) : null;
if(node){
updateTip(tip, node.attributes);
} else {
return false;
}
}

function beforeGridTipShow(tip) {
var rec = this.host.getStore().getAt(this.host.getView().findRowIndex(tip.triggerElement));
if (rec){
updateTip(tip, rec.data);
} else {
return false;
}
}

function beforeViewTipShow(tip) {
var rec = this.host.getRecord(tip.triggerElement);
if (rec){
updateTip(tip, rec.data);
} else {
return false;
}
}

function beforeFormTipShow(tip) {
var el = Ext.fly(tip.triggerElement).child('input,textarea'),
field = el ? this.host.getForm().findField(el.id) : null;
if (field && (field.tooltip || tip.tpl)){
updateTip(tip, field.tooltip || field);
} else {
return false;
}
}

function beforeComboTipShow(tip) {
var rec = this.host.store.getAt(this.host.selectedIndex);
if (rec){
updateTip(tip, rec.data);
} else {
return false;
}
}

return {
init: function(host) {
host.dataTip = this;
this.host = host;
if (host instanceof Ext.tree.TreePanel) {
this.delegate = this.delegate || 'div.x-tree-node-el';
this.on('beforeshow', beforeTreeTipShow);
} else if (host instanceof Ext.grid.GridPanel) {
this.delegate = this.delegate || host.getView().rowSelector;
this.on('beforeshow', beforeGridTipShow);
} else if (host instanceof Ext.DataView) {
this.delegate = this.delegate || host.itemSelector;
this.on('beforeshow', beforeViewTipShow);
} else if (host instanceof Ext.FormPanel) {
this.delegate = 'div.x-form-item';
this.on('beforeshow', beforeFormTipShow);
} else if (host instanceof Ext.form.ComboBox) {
this.delegate = this.delegate || host.itemSelector;
this.on('beforeshow', beforeComboTipShow);
}
if (host.rendered) {
onHostRender.call(host);
} else {
host.onRender = host.onRender.createSequence(onHostRender);
}
}
};
})());


Then just configure a Tree with an instance as a plugin. Provide a tpl (http://www.extjs.com/deploy/dev/docs/?class=Ext.Component&member=tpl) which uses node attribute (http://www.extjs.com/deploy/dev/docs/?class=Ext.tree.TreeNode&member=attributes) values.



plugins: new Ext.ux.DataTip({
tpl: '<div class="whatever">{attributeName}</div>'
})


Or configure a DataView or GridPanel with an instance as a plugin, and the tpl will use the Record the mouse is over as a data source.

steffenk
10 Mar 2010, 12:54 AM
Cool, thx for this!

It should be easy to adapt this to other components as well (grid etc)

Animal
10 Mar 2010, 1:49 AM
Done. Post #1 updated. It works for TreePanel, GridPanel and DataView (ListView is a DataView, so it will work for that too)

evant
10 Mar 2010, 2:21 AM
As always, useful and cool.

Animal
11 Mar 2010, 3:53 AM
Update. This can be configured into any Component now. It just won't self-update unless it is a data-backed Component.

The Component will fire "tip" + event names to pass on the events.

So you would subscribe to the "beforeshowtip" event to poke in any kind of complex data you like.

nicobarten
11 Mar 2010, 4:35 AM
Thanks! Very useful for my ListView.

Just a question. On some website (can't show it because you need a password) i saw also a kind of grid or list, and when i hovered my mouse over an item (let's say for a few seconds), it showed a 'loading'-image next to the item, and after it was loaded it showed a big tooltip.

So, with this DataTip i get the information i want out of the ListView's store. Is there also a way like the method i described above to get specific information of the hovered item out of the database only when the mouseenter (or hover, whatever) gets called?

So i have a ListView's store with 2 columns, 'ID' and 'Name'. Now when i hover over an item in the list, can i make it so that it gets data like 'birthday, address, etc' out of the database for the item's ID and shows it in the DataTip?

Animal
11 Mar 2010, 6:56 AM
Wouldn't it be a waste making a little Ajax call on every mouseover? Just have the data in the Record ready to go.

Animal
16 Mar 2010, 12:43 AM
It works for FormPanels too now. You can configure input fields with tooltip: 'Some text to display'

Animal
22 Mar 2010, 12:41 AM
Works for Combooxes now. Displays details pulled from the selected Record/

senacle
24 Mar 2010, 4:46 AM
Hello,

Here's the description of the bug.

Ext version tested:

Ext 3.2RC1


Adapter used:

ext


css used:

only default ext-all.css
custom css (include details)




Browser versions tested against:

IE6
IE7
IE8
FF3 (firebug 1.5.3 installed)


Operating System:

WinXP Pro


Description:

I've installed your plugin.
See the post : http://www.extjs.com/forum/showthread.php?t=94365&highlight=senacle


Steps to reproduce the problem:

Pass the mouse over a field without tooltip ==> Bug
Then pass the mouse over a field with a tooltip
Last pass the mouse over a field without a tooltip ==> No more bug


The result that was expected:

No error message


The result that occurs instead:

Under FF : this.body is undefined
var bw = this.body.getTextWidth();
ext-all-debug.js (ligne 48221)

Under IE : this.body is null or not an object
ext-all-debug.js (ligne 48221)

scottlusk.cis
9 Apr 2010, 8:43 AM
I'm experiencing the same problem as described by senacle, has anyone found a solution for this? Thanks.

Vidhur
11 May 2010, 7:03 PM
Hi,
I am pretty new to Ext, so this may be a lame question.
I have been trying to use this plugin for a tree, which has certain group nodes and for each group nodes there are certain member nodes. So the attributes for the group node and the member node are different, So how can i utilize this plugin to display tooltip basing on the type of the node I am in? Can I mention any "if" conditions to check the node attribute and if its length is greater than 0, then show certain markup else show something else?

Thanks,
Vidhur

Animal
11 May 2010, 11:38 PM
OK you lazy shower.

I'll debug your free code that you are using. It's not very difficult.

It's a bug in Ext.Tip and Ext.ToolTip

The showAt and show methods of those assume that the superclass.show() call has worked and shown (therefore rendering the Tip).

The DataTip's "beforeshow" event may veto the show, in which case further operations are not needed.

You need



Ext.override(Ext.Tip, {
showAt : function(xy){
Ext.Tip.superclass.show.call(this);
if (this.isVisible()) {
if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){
this.doAutoWidth();
}
if(this.constrainPosition){
xy = this.el.adjustForConstraints(xy);
}
this.setPagePosition(xy[0], xy[1]);
}
}
});

Ext.override(Ext.ToolTip, {
show : function(){
if(this.anchor){
// pre-show it off screen so that the el will have dimensions
// for positioning calcs when getting xy next
this.showAt([-1000,-1000]);
this.origConstrainPosition = this.constrainPosition;
this.constrainPosition = false;
this.anchor = this.origAnchor;
}
this.showAt(this.getTargetXY());
if (this.isVisible()) {
if(this.anchor){
this.syncAnchor();
this.anchorEl.show();
this.constrainPosition = this.origConstrainPosition;
}else{
this.anchorEl.hide();
}
}
}
});


10 minutes work, and you've been hanging around waiting for me to do it for how long?

Sesshomurai
19 May 2010, 4:57 AM
I'll refrain from debugging this request so I don't inject another bug into it! hehe.

Its minor, but the DataTip will display offscreen if the mouse over element is near the edge of the screen.

If anyone gets bored and wants to fix this (original author included), that'd be great. I'd do it, but haven't the time for now. Maybe later.

steffenk
19 May 2010, 5:04 AM
Animal, you never should forget that your 10 minutes debug is 10 hours for other users ;) You are such familar with core that you know where to look and where to break.

Animal
19 May 2010, 6:06 AM
@Sessho: Configure it with



constrainPosition: true

choykawairicky
26 May 2010, 4:37 AM
Hi all, I applied this plugin then got an error of 'this.body is undefined' from the following code, can somebody pls help?




Ext.ns("Ops.setting.cmp");
Ops.setting.cmp.xyz = Ext.extend(Ext.form.CompositeField, {
fieldLabel: 'xyz',
initComponent: function(){
this.aaa = new Ext.form.TextField({
flex:1,
tooltip:'This tip is not showing at all'
});
Ext.apply(this, {
items: [{
xtype:'checkbox',
width: 20,
checked: true,
flex:1,
inputValue: 1
},this.aaa]
});
Ops.setting.cmp.xyz.superclass.initComponent.apply(this, arguments);
}
});
Ext.reg('xyz', Ops.setting.cmp.xyz);



/*!
* Ext JS Library 3.2.1
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
Ext.onReady(function(){

Ext.QuickTips.init();

// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

var bd = Ext.getBody();

/*
* ================ Simple form =======================
*/
bd.createChild({tag: 'h2', html: 'Form 1 - Very Simple'});


var simple = new Ext.FormPanel({
labelWidth: 75, // label settings here cascade unless overridden
url:'save-form.php',
frame:true,
title: 'Simple Form',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',
plugins: [new Ext.ux.DataTip()],
items: [{
fieldLabel: 'xyz',
name: 'xyz',
xtype: 'xyz'
},{
fieldLabel: 'First Name',
name: 'first',
allowBlank:false
},{
fieldLabel: 'Last Name',
name: 'last'
},{
fieldLabel: 'Company',
name: 'company'
}, {
fieldLabel: 'Email',
name: 'email',
vtype:'email'
}, new Ext.form.TimeField({
fieldLabel: 'Time',
name: 'time',
minValue: '8:00am',
maxValue: '6:00pm'
})
],

buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});

simple.render(document.body);

});

Condor
26 May 2010, 5:24 AM
What does this have to do with DataTip?

You want to show a tooltip on an Ext.form.TextField, but that component doesn't have a 'tooltip' config option!

You can attach the tooptip to the el on render or you could use Animal's FieldTip plugin.

choykawairicky
26 May 2010, 8:54 AM
Tks for your input Condor. If I don't get it wrong, there's a line of comment by Animal...

When applied to a FormPanel, this ToolTip appears when over a Field, and the Field's <code>tooltip</code> property is used is applied

so I just install this plugin to my formpanel to test...and got error...

choykawairicky
27 May 2010, 11:06 PM
kindly bump~

thirstypixel
21 Jun 2010, 9:38 AM
Hey Guys,

I have been trying to figure out how to pass in a param specifying a certain cell to spawn the tooltip, I do not want to show it on the entire row.

Does anyone have any ideas on how to accomplish this?

Saint Father
1 Jul 2010, 4:30 AM
may be
http://www.sencha.com/learn/Extension:CellToolTips

thirstypixel
2 Jul 2010, 9:43 AM
Saint Father... this is exactly what I was looking for, just tried it out and it works perfectly. Thanks a ton for the link!

Saint Father
4 Jul 2010, 9:48 PM
I reply your thanks to the author (http://www.sencha.com/forum/member.php?47325-BitPoet) ...
Let the Lord stores his head!
:)

mdissel
7 Jul 2010, 4:55 AM
hmm I still get an error "this.anchorEl is undefined Line 48951" ( on Ext.ToolTip.showAt) when beforeshow is returning false..

(using Ext 3.2.2)

(using the plugin on a treeview and for some nodes i return false from the beforeTreeTipShow(..))

masterjrock
21 Jul 2010, 5:10 AM
the DataTip is excellent, too bad I can't use DataViewTransition plugin with it, the tip record still sticks to the initial sort order.

any ideas how to solve this :(

Animal
21 Jul 2010, 6:55 AM
Set a breakpoint in the code, and see where the mistake is when getting the associated Record?

ExtSwede
28 Sep 2010, 5:58 AM
Just wondering what is the least version you should be using when using this plugin? We still have some applications that hasn't been uppgraded to the latest versions, and that won't be happening unless it's really needed.

SabaSarwat
9 Jan 2011, 10:48 PM
Hello,
I am having some problem in using tooltip in my application. Code is like...


new Ext.FormPanel({
autoScroll: true,
border: false,
title: 'Form',
plugins: new Ext.ux.DataTip({
anchor: 'right',
trackMouse: true
}),
items: [{
xtype: 'containerxtype',
layout: 'hbox',
border: false,
fieldLabel : 'Full Name',
items : [ {
id: 'firstName',
name : 'firstName',
xtype : 'textfieldxtype',
tooltip: 'First Name',
maxLength: 24,
flex : 1
}, space(),{
name : 'middleInitial',
xtype : 'textfieldxtype',
tooltip: 'Middle Initial',
flex : 1,
maxLength: 24
}, space(),{
emptyText : 'Last Name',
name : 'lastName',
id: 'lastName',
tooltip: 'Last Name',
xtype : 'textfieldxtype',
allowBlank : false,
flex : 1,
maxLength: 24
} ]
}],

buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});


The three of my textfields are showing the same tooltip 'First Name', tell me how can i solve this issue.
Further i am also getting this.body error when running my application.

Waiting for reply,
Regards!

SabaSarwat
11 Jan 2011, 3:58 AM
I am still stuck in this problem. Can anyone kindly help me?
Any suggestion condor and animal?
I shall be very thankful.

senacle
21 Jan 2011, 2:43 AM
xtype : 'textfieldxtype'
What is this ?


xtype : 'textfield' is surely better !

senacle
21 Jan 2011, 2:54 AM
I upgrade my code from Extjs 3.2.1 to Extjs 3.3.0 and the Datatip doesn't work anymore for combobox.

There's no error message.

SabaSarwat
21 Jan 2011, 3:19 AM
textfieldxtype is my own xtype.

senacle
31 Jan 2011, 7:25 AM
I'm coming back with this.
So, i put in my code your debugged code.




Ext.override(Ext.Tip, {
showAt : function(xy){
Ext.Tip.superclass.show.call(this);
if (this.isVisible()) {
if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){
this.doAutoWidth();
}
if(this.constrainPosition){
xy = this.el.adjustForConstraints(xy);
}
this.setPagePosition(xy[0], xy[1]);
}
}
});

Ext.override(Ext.ToolTip, {
show : function(){
if(this.anchor){
// pre-show it off screen so that the el will have dimensions
// for positioning calcs when getting xy next
this.showAt([-1000,-1000]);
this.origConstrainPosition = this.constrainPosition;
this.constrainPosition = false;
this.anchor = this.origAnchor;
}
this.showAt(this.getTargetXY());
if (this.isVisible()) {
if(this.anchor){
this.syncAnchor();
this.anchorEl.show();
this.constrainPosition = this.origConstrainPosition;
}else{
this.anchorEl.hide();
}
}
}
});


But now, there's this error :
this.anchorEl is undefined ext-all-debug.js (ligne 32666)
coming from the ligne "this.showAt([-1000,-1000]);" above.

I've tried to understand what's happening, but can't debugg.

So, if you can do a new change, it will be very helpfull.

MrSparks
27 May 2011, 3:39 AM
@Animal,

This is a such a nice UX! Wondered if you planned to update / release a version that is 4.x compatible?

JNason
16 Jan 2012, 1:13 PM
I'm coming back with this.
So, i put in my code your debugged code.


But now, there's this error :
this.anchorEl is undefined ext-all-debug.js (ligne 32666)
coming from the ligne "this.showAt([-1000,-1000]);" above.

I've tried to understand what's happening, but can't debugg.

So, if you can do a new change, it will be very helpfull.

I get the same error as well. Just added this so I haven't tried to debug yet.

JNason
23 Jan 2012, 7:33 AM
I added the showAt override to the ToolTip override that Animal posted and it fixed the this.anchorEl is undefined bug I was getting.


Ext.override(Ext.ToolTip, {
show : function(){
if(this.anchor){
// pre-show it off screen so that the el will have dimensions
// for positioning calcs when getting xy next
this.showAt([-1000,-1000]);
this.origConstrainPosition = this.constrainPosition;
this.constrainPosition = false;
this.anchor = this.origAnchor;
}
this.showAt(this.getTargetXY());
if (this.isVisible()) {
if(this.anchor){
this.syncAnchor();
this.anchorEl.show();
this.constrainPosition = this.origConstrainPosition;
}else{
this.anchorEl.hide();
}
}
},
showAt : function(xy){
this.lastActive = new Date();
this.clearTimers();
Ext.ToolTip.superclass.showAt.call(this, xy);
if(this.dismissDelay && this.autoHide !== false){
this.dismissTimer = this.hide.defer(this.dismissDelay, this);
}
if(this.anchor && !this.anchorEl.isVisible()){
this.syncAnchor();
this.anchorEl.show();
//} else{
} else if (this.anchorEl){ //added check for this.anchorEl
this.anchorEl.hide();
}
},
});

bariand
23 Jan 2013, 2:18 AM
Hi,
is it possible to extend the DataTip extension to have tooltips for radio buttons?
I searched the forum but the solution registering QuickTips doesn't work for me.

Thanks,
Andrea

Richie1985
23 Apr 2013, 9:36 AM
hi, i want to do something like this:


plugins: new Ext.ux.DataTip({
//tpl: '<div class="whatever">{fullname2}</div>'
showDelay: 2000,
trackMouse: true,
tpl: '<div><img height=100% src="images/user_{id}.png"><div>'
})

is there a option to preload the image? because first show of tip is empty, i have to move over the node of my tree a second time to see the pic.

thx!

MrSparks
23 Jul 2013, 12:24 AM
A 4.x version of this plugin now ships as standard with EXTJS 4.2 and above

Located : /extjsextractedpath/examples/ux/DataTip.js

halcwb
14 Oct 2013, 12:08 AM
I use the DataTip in a grid view to dynamically show additional information. This information should be shown only at some nodes, so, I look at the DataTip data to determine whether the tip should be shown or not during the beforeshow event. This works fine with the first visible tip, but then afterwards, the data prop of the DataTip is not updated any more. This is due to the following code in DataTip:



function updateTip(tip, data) {
// Adding the below line solves the problem
tip.data = data;
if (tip.rendered) {
if (tip.host.fireEvent('beforeshowtip', tip.eventHost, tip, data) === false) {
return false;
}
tip.update(data);
} else {
if (Ext.isString(data)) {
tip.html = data;
} else {
tip.data = data;
}
}
}




The thing I don't get is that if DataTip is not rendered, tip.data is set to an object (if data is not a string). But then afterwards, tip is updated by data, whether or not it is a string, which doesn't seem logical to me. A simple fix is to set data any way. This enables me to look at the data (which is a tree record), load the associated information and update the tip with that data.

Is this a bug, or am I not using the DataTip in the right way?

halcwb
15 Nov 2013, 6:13 AM
I use the DataTip in a grid view to dynamically show additional information. This information should be shown only at some nodes, so, I look at the DataTip data to determine whether the tip should be shown or not during the beforeshow event. This works fine with the first visible tip, but then afterwards, the data prop of the DataTip is not updated any more. This is due to the following code in DataTip:



function updateTip(tip, data) {
// Adding the below line solves the problem
tip.data = data;
if (tip.rendered) {
if (tip.host.fireEvent('beforeshowtip', tip.eventHost, tip, data) === false) {
return false;
}
tip.update(data);
} else {
if (Ext.isString(data)) {
tip.html = data;
} else {
tip.data = data;
}
}
}




The thing I don't get is that if DataTip is not rendered, tip.data is set to an object (if data is not a string). But then afterwards, tip is updated by data, whether or not it is a string, which doesn't seem logical to me. A simple fix is to set data any way. This enables me to look at the data (which is a tree record), load the associated information and update the tip with that data.

Is this a bug, or am I not using the DataTip in the right way?

Also, how should I override my fixed updateTip, as the original function is not accessible by being a closure?

senacle
20 Jun 2014, 5:04 AM
I upgrade my code from Extjs 3.2.1 to Extjs 3.3.0 and the Datatip doesn't work anymore for combobox.

There's no error message.

Some precision : the Datatip is working for the items of the combobox list, but not for the combo itself.

I've found the reason.
My combobox has the property hiddenName.
When the mouse goes on the combobox, the first element read in the DOM is the hidden field with hiddenname.
And because it's hidden, it can't display tooltip.

I've found this solution : put a listener in the property of the combobox (http://stackoverflow.com/questions/21228172/extjs-3-4-set-tool-tip-for-combo-box)


listeners: {
render: function(c) {
new Ext.ToolTip({
target: c.getEl(),
html: 'Tooltip content'
});
}
},