Extjs Ext.ComboBox autosize over existing content - javascript

I have a problem when apply an Ext.ComboBox over an existing html select item, even if the existing content makes the html select about 20px (by it's content non static width is set), the Ext.ComboBox will resize to a sort of default, large, width value.
There's a way to auto resize the Ext.ComboBox based on the existing items and no using the default width?
Even if I know which best tool Ext is, this issue will let my colleagues to discard Extjs.
Thanks in advance

You can't technically make a combo "auto width" -- Ext actually converts the <select> into a regular <input> behind the scenes, and <input> elements have to have a width/size specified. However, you can trick Ext into sizing the combo based on the existing <select> which should give you the same end result. Here's an example from the Ext combo demo page, where I have modified the width config value:
var converted = new Ext.form.ComboBox({
typeAhead: true,
triggerAction: 'all',
transform:'state',
width: Ext.fly('state').getWidth(),
forceSelection:true
});
The obvious caveat would be that if you subsequently modify the list after it's rendered, the combo will not resize itself automatically and you'd have to figure out a way to resize it yourself.

Use this code:
Ext.ux.ResizableComboBox = Ext.extend(Ext.form.ComboBox, {
initComponent: function(){
Ext.ux.ResizableComboBox.superclass.initComponent.call(this);
this.on('render', this.resizeToFitContent, this);
},
resizeToFitContent: function(){
if (!this.elMetrics){
this.elMetrics = Ext.util.TextMetrics.createInstance(this.getEl());
}
var m = this.elMetrics, width = 0, el = this.el, s = this.getSize();
this.store.each(function (r) {
var text = r.get(this.displayField);
width = Math.max(width, m.getWidth(text));
}, this);
if (el) {
width += el.getBorderWidth('lr');
width += el.getPadding('lr');
}
if (this.trigger) {
width += this.trigger.getWidth();
}
s.width = width;
this.setSize(s);
this.store.on({
'datachange': this.resizeToFitContent,
'add': this.resizeToFitContent,
'remove': this.resizeToFitContent,
'load': this.resizeToFitContent,
'update': this.resizeToFitContent,
buffer: 10,
scope: this
});
}
});Ext.reg('resizable-combo', Ext.ux.ResizableComboBox);

In addition to what bmoeskau suggests, you can use an xtemplate for your combo's items. This will give you the ability to change the look of the item. You can wrap text, add images, etc.

add a listener to the afterrender event and set the width if the list (the div that drops down ) to auto e.g.
afterrender: function(combo){
combo.list.setSize('auto', 0);
combo.innerList.setSize('auto', 0);
}
The reason I am using afterrender and not render is because if you set lazyInit to false it will set the list width, so in afterrender you override that setWidth

I'm pretty sure you can get ExtJs to render whatever html items you need to, in the way you want them to be rendered.
here's some code from the Examples/Form/Combos.js file:
var converted = new Ext.form.ComboBox({
typeAhead: true,
triggerAction: 'all',
transform:'state',
width:20, //<-- set this config value!
forceSelection:true
});
in the code that you're using to transform the combo, just specify a width for the ExtJs combo.

Related

How to lock slider and prevent updating of values with mouse into dat.GUI menu

I try to implement a way to prevent the updating of values with mouse (actually when the three.js animation has started, launched with a click on button).
For the moment, I have the following dat.GUI menu:
Once "start" button is clicked, I would like to prevent user from modifying with mouse the parameters "Rotation x" and "Rotation y".
Here is the concerned part of code for this menu:
// Create GUI
var gui = new dat.GUI({
autoplace: false,
width: 350,
height: 9 * 32 - 1
});
var params = {
GreatCircle : '',
Rotationx : torusRotationInitX,
Rotationy : torusRotationInitY,
StartingVector : '',
ComponentVectorTheta : 15.0,
ComponentVectorPhi : 15.0,
CovariantDerivativeVector : '',
ComponentCovariantDerivativeTheta : 15.0,
ComponentCovariantDerivativePhi : 15.0
};
// Set parameters for GUI
gui.add(params, 'GreatCircle').name('Great Circle ');
controllerRotationx = gui.add(params, 'Rotationx', 0, 2*Math.PI, 0.001).name('Rotation x ');
controllerRotationy = gui.add(params, 'Rotationy', 0, 2*Math.PI, 0.001).name('Rotation y ');
...
When I click on reset button, I call the following function:
// Reset Button
resetButton.onclick = function ResetParameters() {
...
// Reinitialize parameters into gui
params.Rotationx = torusRotationInitX;
params.Rotationy = torusRotationInitY;
for (var i in gui.__controllers) {
gui.__controllers[i].updateDisplay();
}
render();
}
I don't know if there is an option for controller to lock these sliders which usually change their values. Is it possible?
Update 1
Maybe I could wrapper the dat.GUI menu into a div and make this div not clickable, is it a solution?
Update 2
I tried to apply the method used on Method for disabling a button in dat.gui?
Following this solution, I have added the extension into dat.gui, just after:
dat.controllers.FunctionController = (function (Controller, dom, common) {
...
});
The following added code snippet is:
function blockEvent(event)
{
event.stopPropagation();
}
Object.defineProperty(dat.controllers.FunctionController.prototype, "disabled", {
get: function()
{
return this.domElement.hasAttribute("disabled");
},
set: function(value)
{
if (value)
{
this.domElement.setAttribute("disabled", "disabled");
this.domElement.addEventListener("click", blockEvent, true);
}
else
{
this.domElement.removeAttribute("disabled");
this.domElement.removeEventListener("click", blockEvent, true);
}
},
enumerable: true
});
Is extension code well located into dat.GUI source?
Then, I set the property "disabled" into my code to prevent user from sliding "controllerRotationx" with mouse (once start button is pressed):
if (animation)
controllerRotationx.__li.disabled = true;
Unfortunately, my method doesn't work : when animation is started, I can still move the slider contained into "controllerRotationx".
I saw that above link (Method for disabling a button in dat.gui?), this was about a button and not for a slider, does it change anything for my case?
I didn't find an explicit controller for the slider.
I would do this. The slider is not a form element, there's nothing to disable in the traditional w3c sense. Luckily we can use pointer-events and disable it properly as if it were a form element using just public dat.gui properties.
var speeder = menu.add(text, 'speed', -5, 5);
speeder.domElement.style.pointerEvents = "none"
speeder.domElement.style.opacity = .5;
The solution given by #Radio works pretty well. But, with sliders, the slider is a sibling of the text box's DOM element. We need to disable pointer events on the div which contains all the controls (and which is not exposed directly by dat.gui). So,
var speeder = menu.add(text, 'speed', -5, 5);
// disables the text box
speeder.domElement.style.pointerEvents = "none"
// disables all controller elements related to "speeder"
speeder.domElement.parentElement.style.pointerEvents = 'none'
When the Start button is pressed, set:
controllerRotationx.__li.setAttribute( "style", "display: none" );
thanks for tips
on my side i hack the Common controller
so able to chainning.
gui.add(this, '_screenW').disable(true);
Common.extend(controller, {
disable: function disable(v) {
this.domElement.style.pointerEvents = v?"none":"auto";
this.domElement.style.opacity = v?.5:1;
return controller;
},

Highcharts - how can I center labels on a datetime x-axis?

I was having a hard time trying to figure out how to center labels on a datetime x-axis in Highcharts without using categories and tickPlacement (since tickPlacement only works on categories).
My axis was dynamically created so I could not simply set an x-offset or padding, as this would cause axes of different intervals to look strange.
After messing around with the config options I think I may have found a solution using the x-axis formatter and some css / jquery noodling in the Highcharts callback. See my answer below.
The trick is to use the x-axis labels object like this:
xAxis: {
type: 'datetime',
labels: {
useHTML: true,
align: 'center',
formatter: function () {
//using a specific class for the labels helps to ensure no other labels are moved
return '<span class="timeline_label">' + Highcharts.dateFormat(this.dateTimeLabelFormat, this.value) + '</span>';
}
}
You can see that the formatter will keep whatever dateTimeLabelFormat has been set by the user or default.
Then have a callback that does something like this:
function (chart) {
var $container = $(chart.container);
var $labels = $container.find('.highcharts-axis-labels .timeline_label');
var $thisLabel, $nextLabel, thisXPos, nextXPos, delta, newXPos;
$labels.each(function () {
$thisLabel = $(this).parent('span');
thisXPos = parseInt($thisLabel.css('left'));
$nextLabel = $thisLabel.next();
nextXPos = $nextLabel.length ? parseInt($nextLabel.css('left')) : chart.axes[0].left + chart.axes[0].width;
delta = (nextXPos - thisXPos) / 2.0;
newXPos = thisXPos + delta;
if ($nextLabel.length || $(this).width() + newXPos < nextXPos) {
$thisLabel.css('left', newXPos + 'px');
} else {
$thisLabel.remove();
}
});
});
In short, this will go through each label and determine how much it should be moved over (using css) by calculating the distance between itself and the next label. When it reaches the the last label, it either moves it over using the end of the axis for the calculation or removes it if it won't fit. This last part is just the decision I decided to make, you can probably choose to do something else like word wrap, etc.
You can see the jsfiddle here
Hope this helps some people. Also, if there are any improvements it would be great to see them here.
Based on the existing answer, there is a much simpler solution that also works when resizing the browser window (or otherwise forcing the chart to redraw), even when the tick count changes: http://jsfiddle.net/McNetic/eyyom2qg/3/
It works by attaching the same event handler to both the load and the redraw events:
$('#container').highcharts({
chart: {
events: {
load: fixLabels,
redraw: fixLabels
}
},
[...]
The handler itself looks like this:
var fixLabels = function() {
var labels = $('div.highcharts-xaxis-labels span', this.container).sort(function(a, b) {
return +parseInt($(a).css('left')) - +parseInt($(b).css('left'));
});
labels.css('margin-left',
(parseInt($(labels.get(1)).css('left')) - parseInt($(labels.get(0)).css('left'))) / 2
);
$(labels.get(this.xAxis[0].tickPositions.length - 1)).remove();
};
As you see, the extra wrapping of labels is unnecessary (at least if you do not have more than one xAxis). Basically, it works like this:
Get all existing labels (when redrawn, this includes newly added ones). 2. Sort by css property 'left' (they are not sorted this way after some redrawing)
Calculate offset between the first two labels (the offset is the same for all labels)
Set half of the offset as margin-left of all labels, effectively shifting them half the offset to the right.
Remove the rightmost label (moved outside of chart, by sometimes partly visible).

Add tip text dynamically to a slider

In my project, I am trying to add the tip text (config) dynamically to a slider. How to do that?
I need to add it dynamically because I am generating the array of variables in a "Controller", which holds the text for each value of the slider (for tip text).
var slider = Ext.getCmp('slider')
slider.setTipText(arrayOfVariables) //What should I do here instead?
There is no such method like setTipText in docs. What should I use then?
EDIT:
{
xtype:'slider',
animate: false,
//plugins: [Ext.create('App.view.SliderOverride')],
cls: 'sliderStyle',
width: "80%",
id: 'slider',
value: 36/2, //must be current month
//increment: 10,
minValue: 1,
maxValue: 36,
useTips: true,
tipText: function(thumb){
alert("hello");
App.getController('TaskController')._arrMonthView[thumb.value].month;
},
},
tipText requires a function config so you can add a function that will use your variables from controller;
Ext.create('Ext.slider.Multi', {
....
tipText: function(){
return App.getController('your controller').yourVariable
},
.....
});
This is added on the creation of the slider so you don't need to modify it , just your variables in controller. So you don't need to re set the tip text function.
I solved this issue by using getText method of Ext.slider.Tip.
Used to create the text that appears in the Tip's body. By default this just returns the value of the Slider Thumb that the Tip is attached to. Override to customize.
For example in which situation it can be used, you have a look at this link

ExtJs4 - AutoComplete ComboBox not displaying Empty Text

I have a combo-box with following configuration.
{
fieldLabel:'Service',
xtype:'combo',
displayField: 'srvcDesc',
store: storeServiceCodeVar,
valueField:'srvcCD',
id:'serviceCodeId',
name:'serviceCodeName',
queryMode: 'remote',
queryDelay:100,
typeAhead: true,
minChars:0,
hideTrigger:true,
forceSelection:true,
maxHeight:23,
deferEmptyText:false,
autoSelect:true,
fieldStyle:'text-transform:uppercase',
listConfig: {
loadingText: 'Loading...',
// Custom rendering template for each item
getInnerTpl: function() {
return '<table width="200px"><tr><td height="5"></td></tr><tr valign="top"><td>Code:{srvcCD}</td></tr><tr><td height="2"></td></tr><tr valign="top"><td>Description:{srvcDesc}</td></tr><tr><td height="5"></td></tr></table>';
},
emptyText:'No Values Found'
}
}
The issue is that when there is no data returned from the server, then emptyText (which has value - No values found) gets displayed for may be a millisecond and goes off. I want it to stay there till the next query if fired. How is it possible. I have tried with deferEmptyText but no luck.
Could someone throw some light on this. I am using ExtJS 4 and behavior is same in IE9 and Mozilla.
Thanks in advance.
From stepping through the source, it doesn't seem like there is any reference to the listConfig.emptyText being used to determine whether or not to set the element's height to a number other than zero.
I've ended up overriding the alignPicker() function which Ext.form.field.ComboBox inherits from Ext.form.field.Picker, and adding a check for listConfig.emptyText.
Ext.override(Ext.form.field.ComboBox, {
alignPicker: function() {
var picker, height;
if (this.isExpanded) {
// Get the picker component.
picker = this.getPicker();
if (this.matchFieldWidth) {
// Set the default height to null, since we don't
// automatically want to have the height changed.
height = null;
// If our store exists, but the count is zero
// and we've got no emptyText defined...
if (picker.store &&
picker.store.getCount() === 0 &&
Ext.isEmpty(this.listConfig.emptyText)) {
// ...we set the height to zero.
height = 0;
}
// Set the size of the picker component.
picker.setSize(this.bodyEl.getWidth(), height);
}
if (picker.isFloating()) {
this.doAlign();
}
}
}
});
Hope this helps!
A word of warning here. I'm on ExtJs 4-0-6 and it seems that there is now some code in Ext.form.field.ComboBox and it no longer just relies on inheriting the method from Ext.field.form.Picker.
So, instead the above code should now override the code directly in Ext.field.form.Picker rather than in the ComboBox.
But admittedly, hopefully Sencha will fix this themselves soon in 4.1.

How to expand an ExtJS Component to fullscreen and restore it back later?

how can I expand an ExtJS (version 3.3.1) Component, e.g. a Ext.Panel nested somewhere in the document hierarchy to "fullscreen" so that it takes up the whole browser window region? I guess I need to create an Ext.Viewport dynamically and reparent the component being "expanded", but I've had no success so far. Could someone provide a working sample?
Also, I'd like to be able to restore the component to its original place at some point later, if that's at all possible.
I tried the following:
new Ext.Button({ text: 'Fullscreen', renderTo : Ext.getBody(), onClick: function(){
var viewPort = new Ext.Viewport({
renderTo: Ext.getBody(),
layout: "fit",
items: [ panelToBeExpanded ]
});
viewPort.doLayout();
}});
which does not work very well. Upon clicking the button, the panel panelToBeExpanded seems to take up the viewport region, but only if there is no HTML in the BODY section, otherwise viewport is not fully expanded. Also, working with the reparented panel afterwards causes weird flicker in most browsers.
Is there a reliable way to universally (ideally temporarily) expand a component to the whole browser window?
UPDATE
Thanks to a suggestion in the comments, creating a new maximized Ext.Window seems to be a good solution. The second part is a bit tricky though - how to move the reparented component back to its original place in DOM (and ExtJS component hierarchy) once the window is closed?
Thanks for your help!
You could use Ext.Window.toggleMaximize method. I created a simple working example, check it out here
Pay attention that Ext.Window is maximized inside its rendering container, so if you use "renderTo" attribute and set it to some div inside your page Window will only be as big as div that contains it. That is why I used document body to render myWindow. Of course you could also use Ext.Window.x and Ext.Window.y configuration attributes to locate your window in wanted place.
This is a little late but stumbled upon this only now and remembered I had to do something similar and ended up overriding the text-area component which would automatically expand to full-screen on doubleclick by creating a copy of the component in a full-size window. On closing the values are automatically updated in the instantiating component which was hidden behind the full-screen window and hence never was taken out of the dom in the first place.
Here's my code I think it's fairly self-explanatory.
Hope it helps someone!
Rob.
/**
* Override for default Ext.form.TextArea. Growing to near full-screen/full-window on double-click.
*
* #author Rob Schmuecker (Ext forum name rob1308)
* #date September 13, 2010
*
* Makes all text areas enlargable by default on double-click - to prevent this behaviour set "popout:false" in the config
* By default the fieldLabel of the enhanced field is the fieldLabel of the popout - this can be set separately with "popoutLabel:'some string'" this will also inherit the same labelSeparator config value as that of the enhanced parent.
* The close text for the button defaults to "Close" but can be overriden by setting the "popoutClose:'some other text'" config
*/
Ext.override(Ext.form.TextArea, {
popout: true,
onRender: function(ct, position) {
if (!this.el) {
this.defaultAutoCreate = {
tag: "textarea",
style: "width:100px;height:60px;",
autocomplete: "off"
};
}
Ext.form.TextArea.superclass.onRender.call(this, ct, position);
if (this.grow) {
this.textSizeEl = Ext.DomHelper.append(document.body, {
tag: "pre",
cls: "x-form-grow-sizer"
});
if (this.preventScrollbars) {
this.el.setStyle("overflow", "hidden");
}
this.el.setHeight(this.growMin);
}
if (this.popout && !this.readOnly) {
if (!this.popoutLabel) {
this.popoutLabel = this.fieldLabel;
}
this.popoutClose = 'Close';
var field = this;
this.getEl().on('dblclick',
function() {
field.popoutTextArea(this.id);
});
};
},
popoutTextArea: function(elId) {
var field = this;
tBox = new Ext.form.TextArea({
popout: false,
anchor: '100% 100%',
labelStyle: 'font-weight:bold; font-size:14px;',
value: Ext.getCmp(elId).getValue(),
fieldLabel: field.popoutLabel,
labelSeparator: field.labelSeparator
});
viewSize = Ext.getBody().getViewSize();
textAreaWin = new Ext.Window({
width: viewSize.width - 50,
height: viewSize.height - 50,
closable: false,
draggable: false,
border: false,
bodyStyle: 'background-color:#badffd;',
//bodyBorder:false,
modal: true,
layout: 'form',
// explicitly set layout manager: override the default (layout:'auto')
labelAlign: 'top',
items: [tBox],
buttons: [{
text: field.popoutClose,
handler: function() {
Ext.getCmp(elId).setValue(tBox.getValue());
textAreaWin.hide(Ext.getCmp(elId).getEl(),
function(win) {
win.close();
});
}
}]
}).show(Ext.getCmp(elId).getEl());
}
});

Categories

Resources