feathersui Drag and Drop in List and connect two Lists - drag-and-drop

below is our attempt to drag and drop list items within the same list-based control. we're trying to mirror the same functionality as "Dragging and dropping in the same control" using flex components: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-7cfd.html.
as the user is re-sorting the list each index will be written to a file so that the re-sorted list will stay as is as the screen is refreshed.
although we can render an avatar and push the dragged object to the end of the list-based control we are unable to drag the object to a specific index within the list-based control.
what we can do: google -> msn -> yahoo -> google
what we can't do: google -> msn -> google -> yahoo
lastly, any ideas on how to store the re-sorted list for a screen refresh would be appreciated.
to build we took the advice about extending the list as well as the advice of others: mironcaius and Josh Tynjala: http://forum.starling-framework.org/topic/defaultlistitemrenderer-does-not-update-lables and http://wiki.starling-framework.org/feathers/drag-drop (see adding display object itself as drag data).
Main.as
package feathers.examples.dragDrop
{
import feathers.data.ListCollection;
import feathers.dragDrop.IDragSource;
import feathers.dragDrop.IDropTarget;
import feathers.themes.AeonDesktopTheme;
import starling.display.Sprite;
import starling.events.Event;
public class Main extends Sprite implements IDragSource, IDropTarget
{
public static var listCollection:ListCollection = new ListCollection([{playerName:"yahoo"},{playerName:"msn"},{playerName:"google"}]);
public function Main()
{
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(event:Event):void
{
new AeonDesktopTheme();
var listTeam1:DragDropList = new DragDropList();
listTeam1.dataProvider = listCollection;
listTeam1.itemRendererProperties.labelField = "playerName";
listTeam1.itemRendererProperties.height = 38;
this.addChild(listTeam1);
//listTeam1.hasElasticEdges = false;
//listTeam1.addEventListener("changeTeam", changeTeamHandler);
}
/*private function changeTeamHandler(event:Event):void
{
//trace(event.target.data);
}*/
}
}
DragDropList.as
package feathers.examples.dragDrop
{
import feathers.controls.Label;
import feathers.controls.List;
import feathers.core.FeathersControl;
import feathers.dragDrop.DragData;
import feathers.dragDrop.DragDropManager;
import feathers.dragDrop.IDragSource;
import feathers.dragDrop.IDropTarget;
import feathers.events.DragDropEvent;
import starling.display.Quad;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
//[Event(name="changeTeam", type="Event")]
public class DragDropList extends List implements IDropTarget, IDragSource
{
public function DragDropList()
{
super();
addEventListener(TouchEvent.TOUCH, touchHandler);
addEventListener(DragDropEvent.DRAG_ENTER, dragEnterHandler);
addEventListener(DragDropEvent.DRAG_DROP, dragDropHandler);
addEventListener(DragDropEvent.DRAG_COMPLETE, dragCompleteHandler);
}
protected function touchHandler(event:TouchEvent):void
{
var touch:Touch = event.touches[0];
if(touch.phase == TouchPhase.BEGAN)
{
if(selectedItem)
{
var fc:FeathersControl = new FeathersControl();
fc.addChild(new Quad(150, 50, 0xFFFFFF));
var lbl:Label = new Label();
lbl.text = selectedItem["playerName"];
fc.addChild(lbl);
var dd:DragData = new DragData();
dd.setDataForFormat("playerFormat", selectedItem);
DragDropManager.startDrag(this, touch, dd, fc);
}
}
}
protected function dragEnterHandler(event:DragDropEvent, dragData:DragData):void
{
if(dragData.hasDataForFormat("playerFormat"))
{
DragDropManager.acceptDrag(this);
}
}
protected function dragDropHandler(event:DragDropEvent, dragData:DragData):void
{
if(dragData.hasDataForFormat("playerFormat"))
{
var obj:Object = dragData.getDataForFormat("playerFormat");
Main.listCollection.push(obj);
//dispatchEventWith("changeTeam", false, obj);
}
}
protected function dragCompleteHandler(event:DragDropEvent):void
{
if(event.isDropped)
{
Main.listCollection.removeItem(selectedItem);
}
}
}
}

I've now got a reasonable implementation of a drag sortable Feathers List working. I'm sure it could be improved, and as an AS3 / Starling / Feathers novice, I'm sure I'll have done some parts in a less than ideal way, but I'm pretty happy with how it works.
The scroll is controlled by detecting whether the drag is close to the top / bottom of the list, in the touchHandler, which initiates a slower scroll, or past the top / bottom of the list, which starts a faster scroll.
Hope it's still of some use for you, and feel free to come back with any questions on it, or advice on improving it!
Ted
Also, in case they're of interest, these are the two drag handle images I've used in the code:
Main.as
package feathers.examples.dragDrop
{
import feathers.controls.Button;
import feathers.controls.List;
import feathers.data.ListCollection;
import feathers.dragDrop.IDragSource;
import feathers.dragDrop.IDropTarget;
import feathers.themes.MetalWorksMobileTheme;
import starling.core.Starling;
import starling.display.Sprite;
import starling.events.Event;
public class Main extends Sprite implements IDragSource, IDropTarget
{
private var list:List;
public function Main()
{
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(event:Event):void
{
new MetalWorksMobileTheme();
const PADDING:int = 100;
list = new List();
list.width = Starling.current.viewPort.width - (PADDING * 2);
list.height = Starling.current.viewPort.height - (PADDING * 2);
this.addChild( list );
list.y = PADDING;
list.x = PADDING;
list.itemRendererType = DragDropItemRenderer;
resetDefaultListData();
var resetButton:Button = new Button();
resetButton.label = "Reset List Data";
resetButton.addEventListener(Event.TRIGGERED, resetButtonHandler);
resetButton.nameList.add(Button.ALTERNATE_NAME_QUIET_BUTTON);
this.addChild(resetButton);
resetButton.validate();
resetButton.x = (Starling.current.viewPort.width / 2) - (resetButton.width / 2);
resetButton.y = Starling.current.viewPort.height - resetButton.height - (PADDING / 5);
}
private function resetButtonHandler(e:Event):void
{
resetDefaultListData();
}
private function resetDefaultListData():void
{
var defaultData:ListCollection = new ListCollection(
[
{text: 'Test Item 1' },
{text: 'Test Item 2' },
{text: 'Test Item 3' },
{text: 'Test Item 4' },
{text: 'Test Item 5' },
{text: 'Test Item 6' },
{text: 'Test Item 7' },
{text: 'Test Item 8' },
{text: 'Test Item 9' },
{text: 'Test Item 10' },
{text: 'Test Item 11' },
{text: 'Test Item 12' },
{text: 'Test Item 13' },
{text: 'Test Item 14' },
{text: 'Test Item 15' },
{text: 'Test Item 16' },
{text: 'Test Item 17' },
{text: 'Test Item 18' },
{text: 'Test Item 19' },
{text: 'Test Item 20' },
{text: 'Test Item 21' },
{text: 'Test Item 22' },
{text: 'Test Item 23' },
{text: 'Test Item 24' },
{text: 'Test Item 25' },
{text: 'Test Item 26' },
{text: 'Test Item 27' },
{text: 'Test Item 28' },
{text: 'Test Item 29' },
{text: 'Test Item 30' },
{text: 'Test Item 31' },
{text: 'Test Item 32' },
{text: 'Test Item 33' },
{text: 'Test Item 34' },
{text: 'Test Item 35' },
{text: 'Test Item 36' },
{text: 'Test Item 37' },
{text: 'Test Item 38' },
{text: 'Test Item 39' },
{text: 'Test Item 40' },
{text: 'Test Item 41' },
{text: 'Test Item 42' },
{text: 'Test Item 43' },
{text: 'Test Item 44' },
{text: 'Test Item 45' },
{text: 'Test Item 46' },
{text: 'Test Item 47' },
{text: 'Test Item 48' },
{text: 'Test Item 49' },
{text: 'Test Item 50' },
{text: 'Test Item 51' },
{text: 'Test Item 52' },
{text: 'Test Item 53' },
{text: 'Test Item 54' },
{text: 'Test Item 55' },
{text: 'Test Item 56' },
{text: 'Test Item 57' },
{text: 'Test Item 58' },
{text: 'Test Item 59' },
{text: 'Test Item 60' },
{text: 'Test Item 61' },
{text: 'Test Item 62' },
{text: 'Test Item 63' },
{text: 'Test Item 64' },
{text: 'Test Item 65' },
{text: 'Test Item 66' },
{text: 'Test Item 67' },
{text: 'Test Item 68' },
{text: 'Test Item 69' },
{text: 'Test Item 70' },
{text: 'Test Item 71' },
{text: 'Test Item 72' },
{text: 'Test Item 73' },
{text: 'Test Item 74' },
{text: 'Test Item 75' },
{text: 'Test Item 76' },
{text: 'Test Item 77' },
{text: 'Test Item 78' },
{text: 'Test Item 79' },
{text: 'Test Item 80' },
{text: 'Test Item 81' },
{text: 'Test Item 82' },
{text: 'Test Item 83' },
{text: 'Test Item 84' },
{text: 'Test Item 85' },
{text: 'Test Item 86' },
{text: 'Test Item 87' },
{text: 'Test Item 88' },
{text: 'Test Item 89' },
{text: 'Test Item 90' },
{text: 'Test Item 91' },
{text: 'Test Item 92' },
{text: 'Test Item 93' },
{text: 'Test Item 94' },
{text: 'Test Item 95' },
{text: 'Test Item 96' },
{text: 'Test Item 97' },
{text: 'Test Item 98' },
{text: 'Test Item 99' },
{text: 'Test Item 100' },
]);
list.dataProvider = defaultData;
}
}
}
DragDropItemRenderer.as
package feathers.examples.dragDrop
{
import flash.events.TimerEvent;
import flash.utils.Timer;
import feathers.controls.Label;
import feathers.controls.List;
import feathers.controls.renderers.IListItemRenderer;
import feathers.core.FeathersControl;
import feathers.dragDrop.DragData;
import feathers.dragDrop.DragDropManager;
import feathers.dragDrop.IDragSource;
import feathers.dragDrop.IDropTarget;
import feathers.events.DragDropEvent;
import starling.display.Button;
import starling.display.DisplayObject;
import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.textures.Texture;
import starling.utils.Color;
public class DragDropItemRenderer extends FeathersControl implements IListItemRenderer, IDragSource, IDropTarget
{
[Embed(source="drag-handle.png")]
public static const DragHandle:Class;
[Embed(source="drag-handle-down.png")]
public static const DragHandleDown:Class;
private const LIST_ITEM_DRAG:String = "LIST_ITEM_DRAG";
public function DragDropItemRenderer()
{
//drag
this.addEventListener(DragDropEvent.DRAG_START, dragStartHandler);
this.addEventListener(DragDropEvent.DRAG_COMPLETE, dragCompleteHandler);
//drop
this.addEventListener(DragDropEvent.DRAG_ENTER, dragEnterHandler);
this.addEventListener(DragDropEvent.DRAG_EXIT, dragExitHandler);
this.addEventListener(DragDropEvent.DRAG_MOVE, dragMoveHandler);
this.addEventListener(DragDropEvent.DRAG_DROP, dragDropHandler);
}
//================================================================================================
//drag interface
//================================================================================================
private function dragStartHandler(event:DragDropEvent, dragData:DragData):void
{
//the drag was started with the call to DragDropManager.startDrag()
}
private function dragCompleteHandler(event:DragDropEvent, dragData:DragData):void
{
if(event.isDropped)
{
//the object successfully dropped at a valid location
}
stopScrolling();
}
private var carryOnScrolling:Boolean=false;
private var scrollDirection:String="";
private var scrollSpeed:String="";
private var scrollTimer:Timer;
private var avatarContainer:Sprite;
private var avatarBg:Quad;
private var avatarLabel:Label;
private const LIST_SCROLL_DETECT_HEIGHT:int = 30;
private const SCROLL_SIZE:int = 25;
private const SCROLL_SPEED_SLOW:String = "SLOW";
private const SCROLL_SPEED_FAST:String = "FAST";
private const SCROLL_DIRECTION_UP:String = "UP";
private const SCROLL_DIRECTION_DOWN:String = "DOWN";
private const SCROLL_TIME_MS_SLOW:int = 75;
private const SCROLL_TIME_MS_FAST:int = 25;
private var scrollSpeedMSToUse:int;
private function startScrollingList(direction:String, speed:String):void
{
scrollDirection = direction;
scrollSpeed = speed;
carryOnScrolling = true;
doScrollWorker();
if(scrollTimer != null)
{
scrollTimer.stop();
scrollTimer = null;
}
if(scrollSpeed == SCROLL_SPEED_FAST)
scrollSpeedMSToUse = SCROLL_TIME_MS_FAST;
else
scrollSpeedMSToUse = SCROLL_TIME_MS_SLOW;
scrollTimer = new Timer(scrollSpeedMSToUse);
scrollTimer.addEventListener(TimerEvent.TIMER, onScrollTimerHandler);
scrollTimer.start();
}
private function stopScrolling():void
{
carryOnScrolling = false;
}
private function onScrollTimerHandler(e:TimerEvent):void
{
if(carryOnScrolling)
{
doScrollWorker();
}
else
{
scrollTimer.stop();
scrollTimer = null;
}
}
private function doScrollWorker():void
{
const EXTRA_SCROLL_PADDING:int = 20;
var scrollSizeToUse:int = SCROLL_SIZE;
if(scrollDirection == SCROLL_DIRECTION_UP)
{
if((this.owner.verticalScrollPosition + scrollSizeToUse) < (this.owner.maxVerticalScrollPosition + EXTRA_SCROLL_PADDING))
{
this.owner.scrollToPosition(this.owner.horizontalScrollPosition, this.owner.verticalScrollPosition + scrollSizeToUse, (scrollSpeedMSToUse / 1000));
}
}
else
{
if((this.owner.verticalScrollPosition - scrollSizeToUse) > (this.owner.minVerticalScrollPosition - EXTRA_SCROLL_PADDING))
{
this.owner.scrollToPosition(this.owner.horizontalScrollPosition, this.owner.verticalScrollPosition - scrollSizeToUse, (scrollSpeedMSToUse / 1000));
}
}
}
private function touchHandler(event:TouchEvent):void
{
if(DragDropManager.isDragging)
{
//check if the drag is close enough to the top / bottom to start scrolling
var touchScrollTest:Touch = event.getTouch(this);
if(touchScrollTest)
{
if(touchScrollTest.globalY < this.owner.y)
{
startScrollingList(SCROLL_DIRECTION_DOWN, SCROLL_SPEED_FAST);
}
else if(touchScrollTest.globalY < this.owner.y + LIST_SCROLL_DETECT_HEIGHT)
{
startScrollingList(SCROLL_DIRECTION_DOWN, SCROLL_SPEED_SLOW);
}
else if(touchScrollTest.globalY > this.owner.y + this.owner.height)
{
startScrollingList(SCROLL_DIRECTION_UP, SCROLL_SPEED_FAST);
}
else if(touchScrollTest.globalY > this.owner.y + this.owner.height - LIST_SCROLL_DETECT_HEIGHT)
{
startScrollingList(SCROLL_DIRECTION_UP, SCROLL_SPEED_SLOW);
}
else
{
stopScrolling();
}
}
//one drag at a time, please
return;
}
if(this._touchID >= 0)
{
var touch:Touch = event.getTouch(this._draggedObject, null, this._touchID);
if(touch.phase == TouchPhase.MOVED)
{
this._touchID = -1;
avatarContainer = new Sprite();
const AVATAR_X_OFFSET:int = 75;
avatarBg = new Quad(this.width - AVATAR_X_OFFSET, this.height, starling.utils.Color.LIME);
avatarBg.alpha = 0.25;
avatarLabel = new Label();
avatarLabel.x = this._label.x;
avatarLabel.y = this._label.y;
avatarLabel.width = this._label.width;
avatarLabel.height = this._label.height;
avatarLabel.text = this._label.text;
avatarContainer.addChild(avatarBg);
avatarContainer.addChild(avatarLabel);
var dragData:DragData = new DragData();
dragData.setDataForFormat(LIST_ITEM_DRAG, this._data);
DragDropManager.startDrag(this, touch, dragData, avatarContainer, -avatarLabel.width + 75, 0);
}
else if(touch.phase == TouchPhase.ENDED)
{
stopScrolling();
this._touchID = -1;
this._bgQuad.color = starling.utils.Color.RED;
}
}
else
{
touch = event.getTouch(this, TouchPhase.BEGAN);
if(!touch || touch.target == this || touch.target == this._background)
{
return;
}
this._touchID = touch.id;
this._draggedObject = touch.target;
}
event.stopPropagation();
}
private var _background:Quad;
private var _touchID:int = -1;
private var _draggedObject:DisplayObject;
//================================================================================================
//================================================================================================
//drop interface
//================================================================================================
private function dragEnterHandler(event:DragDropEvent, dragData:DragData):void
{
if(!dragData.hasDataForFormat(LIST_ITEM_DRAG))
{
return;
}
DragDropManager.acceptDrag(this);
}
private function dragMoveHandler(event:DragDropEvent, dragData:DragData):void
{
var dataBeingDragged:Object = dragData.getDataForFormat(LIST_ITEM_DRAG);
if(dataBeingDragged != this._data)
{
if(event.localY < (this.height / 2))
{
this._hiliteTop.visible = true;
this._hiliteBottom.visible = false;
}
else
{
this._hiliteTop.visible = false;
this._hiliteBottom.visible = true;
}
}
}
private function dragExitHandler(event:DragDropEvent, dragData:DragData):void
{
this._label.text = this._data.text;
this._hiliteTop.visible = false;
this._hiliteBottom.visible = false;
}
private function dragDropHandler(event:DragDropEvent, dragData:DragData):void
{
var dataBeingDragged:Object = dragData.getDataForFormat(LIST_ITEM_DRAG);
if(dataBeingDragged != this._data)
{
this.owner.dataProvider.removeItem(dataBeingDragged);
var indexToDragTo:int = this.owner.dataProvider.getItemIndex(this.data);
if(this._hiliteBottom.visible)
indexToDragTo++;
this.owner.dataProvider.addItemAt(dataBeingDragged, indexToDragTo);
this._hiliteTop.visible = false;
this._hiliteBottom.visible = false;
}
stopScrolling();
}
//================================================================================================
//================================================================================================
//Item renderer code
//================================================================================================
protected var _index:int = -1;
public function get index():int
{
return this._index;
}
public function set index(value:int):void
{
if(this._index == value)
{
return;
}
this._index = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
protected var _owner:List;
public function get owner():List
{
return this._owner;
}
public function set owner(value:List):void
{
if(this._owner == value)
{
return;
}
this._owner = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
protected var _data:Object;
public function get data():Object
{
return this._data;
}
public function set data(value:Object):void
{
if(this._data == value)
{
return;
}
this._data = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
protected var _isSelected:Boolean;
public function get isSelected():Boolean
{
return this._isSelected;
}
public function set isSelected(value:Boolean):void
{
if(this._isSelected == value)
{
return;
}
this._isSelected = value;
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
protected var _label:Label;
protected var _bgQuad:Quad;
protected var _hiliteTop:Quad;
protected var _hiliteBottom:Quad;
protected var _dragHandle:Quad;
protected var _dragHandleButton:starling.display.Button;
override protected function initialize():void
{
this._bgQuad = new Quad(1, 1, starling.utils.Color.SILVER);
this._bgQuad.alpha = 0.5;
this.addChild(_bgQuad);
this._label = new Label();
this.addChild(this._label);
this._hiliteTop = new Quad(100, 2, starling.utils.Color.YELLOW);
this._hiliteTop.visible = false;
this.addChild(_hiliteTop);
this._hiliteBottom = new Quad(100, 2, starling.utils.Color.YELLOW);
this._hiliteBottom.visible = false;
this.addChild(_hiliteBottom);
var dragHandleTexture:Texture = Texture.fromBitmap(new DragHandle());
var dragHandleDownTexture:Texture = Texture.fromBitmap(new DragHandleDown());
_dragHandleButton = new starling.display.Button(dragHandleTexture, "", dragHandleDownTexture);
this.addChild(_dragHandleButton);
_dragHandleButton.addEventListener(TouchEvent.TOUCH, touchHandler);
}
protected function commitData():void
{
if(this._data)
{
this._label.text = this._data.text;
}
else
{
this._label.text = null;
}
}
protected var _padding:Number = 5;
public function get padding():Number
{
return this._padding;
}
public function set padding(value:Number):void
{
if(this._padding == value)
{
return;
}
this._padding = value;
this.invalidate(INVALIDATION_FLAG_LAYOUT);
}
protected function autoSizeIfNeeded():Boolean
{
var needsWidth:Boolean = isNaN(this.explicitWidth);
var needsHeight:Boolean = isNaN(this.explicitHeight);
if(!needsWidth && !needsHeight)
{
return false;
}
this.explicitHeight = 50;
this._label.width = this.explicitWidth - 2 * this._padding;
this._label.height = this.explicitHeight - 2 * this._padding;
this._label.validate();
var newWidth:Number = this.explicitWidth;
if(needsWidth)
{
newWidth = this._label.width + 2 * this._padding;
}
var newHeight:Number = this.explicitHeight;
if(needsHeight)
{
newHeight = this._label.height + 2 * this._padding;
}
return this.setSizeInternal(newWidth, newHeight, false);
}
override protected function draw():void
{
var dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
if(dataInvalid)
{
this.commitData();
}
this.autoSizeIfNeeded();
this.layoutChildren();
}
protected function layoutChildren():void
{
this._label.width = this.actualWidth - (2 * this._padding);
this._label.height = this.actualHeight - (2 * this._padding);
this._label.x = this._padding;
this._label.y = 15;//(this.actualHeight / 2) - (this._label.height / 2);
this._bgQuad.x = this._padding;
this._bgQuad.y = this._padding;
this._bgQuad.width = this.actualWidth - (2 * this._padding);
this._bgQuad.height = this.actualHeight - (2 * this._padding);
this._hiliteTop.x = 0;
this._hiliteBottom.x = 0;
this._hiliteTop.y = 0;
this._hiliteBottom.y = this.height; //this.height - this._hiliteBottom.height;
this._hiliteTop.width = this.width;
this._hiliteBottom.width = this.width;
this._dragHandleButton.x = this.width - this._dragHandleButton.width;
this._dragHandleButton.y = (this.height / 2) - (this._dragHandleButton.height / 2);
}
//================================================================================================
}
}

Related

sencha touch: real time chart

i am using sencha touch to show a chart and add data to store of chart dynamically.but when i add data to store, my chart does not update result.
this is code of my chart:
Ext.define('MyApp.view.MyLineChart1', {
extend: 'Ext.chart.CartesianChart',
requires: [
'Ext.chart.axis.Category',
'Ext.chart.axis.Numeric',
'Ext.chart.series.Line'
],
config: {
itemId: 'xy',
store: 'MyStore',
colors: [
'#115fa6',
'#94ae0a',
'#a61120',
'#ff8809',
'#ffd13e',
'#a61187',
'#24ad9a',
'#7c7474',
'#a66111'
],
axes: [
{
type: 'category',
fields: [
'x'
],
maximum: 5,
minimum: 0
},
{
type: 'numeric',
fields: [
'y'
],
grid: {
odd: {
fill: '#e8e8e8'
}
},
position: 'left'
}
],
series: [
{
type: 'line',
colors: 'rgba(0,200,0,0.3)',
style: {
smooth: true,
stroke: 'rgb(0,200,0)',
},
xField: 'x',
yField: 'y'
}
],
listeners: [
{
fn: 'onChartShow',
event: 'show',
order: 'after'
}
]
},
onChartShow: function(component, eOpts) {
var TaskRunner = Ext.create("MyApp.controller.TaskRunner");
chart = Ext.ComponentQuery.query("#xy")[0];
store = chart.getStore();
chart.animationSuspended = true;
chart.update();
store.removeAll();
this.timeChartTask = TaskRunner.start({
run: this.update_chart,
interval: 1000,
repeat: 10,
scope: this
});
},
update_chart: function(chart) {
var me = this;
chart = Ext.ComponentQuery.query("#xy")[0];
store = chart.getStore();
count = store.getCount();
xAxis = chart.getAxes()[0];
visibleRange = 10000;
second = 1000;
console.log(xAxis.getMinimum());
if (count > 0) {
lastRecord = store.getAt(count - 1);
xValue = lastRecord.get('x') + second;
if (xValue - me.startTime > visibleRange) {
me.startTime = xValue - visibleRange;
xAxis.setMinimum(this.startTime);
xAxis.setMaximum(xValue);
console.log("count >0");
}
store.add({
x: xValue,
y: me.getNextValue()
});
// store.load();
chart.redraw();
} else {
chart.animationSuspended = true;
me.startTime = Math.floor(Ext.Date.now() / second) * second;
xAxis.setMinimum(me.startTime);
xAxis.setMaximum(me.startTime + visibleRange);
store.add({
x: this.startTime,
y: me.getNextValue()
});
chart.animationSuspended = false;
// store.load();
chart.redraw();
console.log("count < 0");
}
},
getNextValue: function(previousValue) {
var delta = Math.random()*4 - 2;
if (Ext.isNumber(previousValue)) {
return Ext.Number.constrain(previousValue + delta, -2, 2);
}
return Math.random()*4 - 2;
}
});
this is my store:
Ext.define('MyApp.store.MyStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.MyModel1'
],
config: {
model: 'MyApp.model.MyModel1',
storeId: 'MyStore'
}
});
and this is my model:
Ext.define('MyApp.model.MyModel1', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.Field'
],
config: {
fields: [
{
name: 'x'
},
{
name: 'y'
}
]
}
});
If you want set data to store use this code:
var store = Ext.getStore('MyStore');
store.load(function () {
var store = Ext.getStore('MyStore');
store.removeAll();
store.add({
x: xValue,
y: me.getNextValue()
});
store.sync();
});

Extjs Cannot read property 'dom' of null

In short, I have a login panel, which will be shown when onClick event is true, but I get error Cannot read property 'dom' of null. When I click second time - the panel is shown, but some elements of it are not shown. When I click for 3th time, the other elements has show, an last, I ger error "Cannot call method 'bringToFront' of undefined".
Ext.onReady(function() {
Ext.QuickTips.init();
Ext.ns('Site.login');
var globalKeyMap = new Ext.KeyMap(document);
globalKeyMap.accessKey = function(key, handler, scope) {
var h = function(n, e) {
e.preventDefault();
handler.call(scope || this, n, e);
};
this.on(key, h, scope);
};
var trackEnter = function(tf, e) {
if (e.keyCode == 13) {
e.stopPropagation();
doLogin();
}
}
var doLogin = function() {
var f = loginPanel.getForm();
if (f.isValid()) {
f.doAction(new Hypo.form.Action.DwrSubmit(f, {
waitMsg: 'Authentication is in progress',
invoker: function(form, cb) {
Login.login(form.getValues(), cb);
},
success: function(fp, o) {
loginForm.hide();
Ext.getBody().addClass("x-body-masked");
var w = loginForm;
w.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
w.mask.show();
if(o.result.data[0].authorities[0].authority=='ROLE_SUPERVISOR')
{
setTimeout('document.location = "AdminUsers.jsp"');
}
else if(o.result.data[0].authorities[0].authority=='ROLE_ADMIN')
{
setTimeout('document.location = "AdminUsers.jsp"');
}
else
{
setTimeout('document.location = "List.jsp"');
}
}
}));
}
}
var loginPanel = new Ext.FormPanel({
id: 'loginPanel',
labelWidth: 75, // label settings here cascade unless overridden
frame: false,
border: false,
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {
width: 230
},
defaultType: 'textfield',
autoHeight: true,
items: [{
id: 'idxLogin',
fieldLabel: 'Login',
name: 'login',
allowBlank: false,
enableKeyEvents: true,
maxLength: 50,
listeners: {
'keypress': trackEnter
}
}, new Ext.form.TextField({
fieldLabel: 'Password',
name: 'password',
inputType: 'password',
enableKeyEvents: true,
maxLength: 50,
listeners: {
'keypress': trackEnter
}
})
]
});
var loginForm = new Ext.Window({
layout : 'fit',
width : 350,
closable : false,
modal: true,
plain : true,
title: 'User Authentication',
items: loginPanel,
autoHeight: true,
buttons: [{
text: 'OK',
handler: doLogin
},{
text: 'Cancel',
handler: function() {
loginForm.hide();
}
}],
listeners: {
'activate': function() {
setTimeout(function() {
Ext.getCmp("idxLogin").focus();
}, 100);
}
}
});
Site.login.window = loginForm;
});
Login
I tryed to add renderTo:'someDiv' and add to body a div with id "someDiv". But I received the same error message.
Please, help with that.

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

Extjs4: editable rowbody

in my first ExtJs4 project i use a editable grid with the feature rowbody to have a big textfield displayed under each row.
I want it to be editable on a dblclick. I succeeded in doing so by replacing the innerHTML of the rowbody by a textarea but the special keys don't do what they are supposed to do (move the cursor). If a use the textarea in a normal field i don't have this problem. Same problem in IE7 and FF4
gridInfo = Ext.create('Ext.ux.LiveSearchGridPanel', {
id: 'gridInfo',
height: '100%',
width: '100%',
store: storeInfo,
columnLines: true,
selType: 'cellmodel',
columns: [
{text: "Titel", flex: 1, dataIndex: 'titel', field: {xtype: 'textfield'}},
{text: "Tags", id: "tags", flex: 1, dataIndex: 'tags', field: {xtype: 'textfield'}},
{text: "Hits", dataIndex: 'hits'},
{text: "Last Updated", renderer: Ext.util.Format.dateRenderer('d/m/Y'), dataIndex: 'lastChange'}
],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
features: [
{
ftype: 'rowbody',
getAdditionalData: function (data, idx, record, orig) {
var headerCt = this.view.headerCt,
colspan = headerCt.getColumnCount();
return {
rowBody: data.desc, //the big textfieldvalue, can't use a textarea here 8<
rowBodyCls: this.rowBodyCls,
rowBodyColspan: colspan
};
}
},
{ftype: 'rowwrap'}
]
});
me.on('rowbodydblclick', function (gridView, el, event, o) {
//...
rb = td.down('.x-grid-rowbody').dom;
var value = rb.innerText ? rb.innerText : rb.textContent;
rb.innerHTML = '';
Ext.create('Ext.form.field.TextArea', {
id: 'textarea1',
value: value,
renderTo: rb,
border: false,
enterIsSpecial: true,
enableKeyEvents: true,
disableKeyFilter: true,
listeners: {
'blur': function (el, o) {
rb.innerHTML = el.value;
},
'specialkey': function (field, e) {
console.log(e.keyCode); //captured but nothing happens
}
}
}).show();
//...
});
damn, can't publish my own solution, looks like somebody else has to answer, anyway, here is the function that works
function editDesc(me, gridView, el, event, o) {
var width = Ext.fly(el).up('table').getWidth();
var rb = event.target;
var value = rb.innerText ? rb.innerText : rb.textContent;
rb.innerHTML = '';
var txt = Ext.create('Ext.form.field.TextArea', {
value: value,
renderTo: rb,
border: false,
width: width,
height: 300,
enterIsSpecial: true,
disableKeyFilter: true,
listeners: {
'blur': function (el, o) {
var value = el.value.replace('\n', '<br>')
rb.innerHTML = value;
},
'specialkey': function (field, e) {
e.stopPropagation();
}
}
});
var txtTextarea = Ext.fly(rb).down('textarea');
txtTextarea.dom.style.color = 'blue';
txtTextarea.dom.style.fontSize = '11px';
}
Hi Molecule Man, as an alternative to the approach above i tried the Ext.Editor.
It works but i want it inline but when i render it to the rowbody, the field blanks and i have no editor, any ideas ?
gridInfo = Ext.create('Ext.grid.Panel', {
id: 'gridInfo',
height: '100%',
width: '100%',
store: storeInfo,
columnLines: true,
selType: 'cellmodel',
viewConfig: {stripeRows: false, trackOver: true},
columns: [
{text: "Titel", flex: 1, dataIndex: 'titel', field: {xtype: 'textfield'}},
//...
{
text: "Last Updated", renderer: Ext.util.Format.dateRenderer('d/m/Y'), dataIndex: 'lastChange'
}
],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
features: [
{
ftype: 'rowbody',
getAdditionalData: function (data, idx, record, orig) {
var headerCt = this.view.headerCt,
colspan = headerCt.getColumnCount();
return {
rowBody: data.desc,
rowBodyCls: this.rowBodyCls,
rowBodyColspan: colspan
};
}
}
],
listeners: {
rowbodyclick: function (gridView, el, event) { //werkt
editDesc(this, gridView, el, event);
}
}
})
;
function editDesc(me, gridView, el, event, o) {
var rb = event.target;
me.txt = new Ext.Editor({
field: {xtype: 'textarea'},
updateEl: true,
cancelOnEsc: true,
floating: true,
renderTo: rb //when i do this, the field becomes empty and i don't get the editor
});
me.txt.startEdit(el);
}
This is just to set the question answered, see solution above

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