My custom Widget have a collection of child widgets that are constructed on AJAX response. So I need to maintain an array of child widgets and append the domNode of those widgets to its parent Widget's domNode.
I can use a dijit.WidgetSet make a collection of the child widgets but there is no dijit.WidgetSet.domNodes() method to get domNodes of all the widgets. also dijit.WidgetSet doesn't take care of placement of child nodes in its parent DomNode
Is there any other class for doing the same already ? e.g. would subclassing dijit.WidgetSet to take care of appending to parent domNode be a reinvetion of Wheel ?
If you extend your parent with dijit._Container you can get hold of your widgetset by calling parent.getChildren(). The order in which widgets are returns is afaik the order that they were added to parent (parent.addChild()) and not by DOM sibling-hood. However, once you use the parser, this ofc would be the same. Well, any widget would have a getChildren implemented but the difference for _Container class is, that the addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex) and removeChild: function(/*Widget|int*/ widget) functions are pretty nifty. If you put an insertIndex while you add children, sibling-hood is manageable.
For concern of domNodes, with dijit we go 'beyond' DOM and work in the widget layer instead - which is only a JS wrap. The chainable function '.domNodes()' does not exist anywhere in dojo, you instead need to call similar to following:
parent.getChildren().forEach(function(childWidget) {
var domNode = childWidget.domNode;
...
});
// this would get nowhere as the return child-set is merely a 'stupid' array
parent.getChildren().set("attribute", "value");
Here is a cute little wrapper for the above foreach, using dijit/map
// retreives an array of the actual domNodes of widgetset
dijit.registry.map(function(widget){return widget.domNode;}).forEach(
// uses hitch to allow passing parameters without wrapping in new function
dojo.hitch(
window, // on random scope
dojo.addClass, // calling function
"imTheChildDomNode"// arguments[0]
)
);
Related
Are there any signals I can bind to that allow me to execute functions directly before and directly after a property changes?
For example something like this:
Canvas {
id: canvas
anchors.fill: parent
property real lastX
property real lastY
property var lastContext
function cacheImage(){
lastContext = canvas.context
}
function loadImageFromCache(){
canvas.context = lastContext
canvas.requestPaint()
}
}
I want to call the cacheImage() function right before the canvas's size changes and the loadImageFromCache() right after the resize is completed.
If I understand correctly the onWidthChanged and onHeightChanged do the latter, but I need to save the image before it does that. Is there a simple way to do this?
But cacheImage() doesn't have to be called right before the size changes! It needs to be called any time after the most recent canvas contents change, and before the new canvas size takes effect. The window to cache the canvas is very long and extends between changes. The event "before n-th property change" is simply "after (n-1)-th property change" :)
This kind of functionality is not part of the "core design intent" of QML, so you are not getting it out of the box.
It is quite easy to do for C++ objects when you get to implement your own setter function.
But even in QML you can simply use an auxiliary setter function rather than using the property directly:
function setSomeProp(newValue) {
if (newValue === oldValue) return
stuffBeforeChange()
oldValue = newValue
stuffAfterChange() // optional, you can use the default provided signal too
}
If you insist on using a property based syntax, you can use an additional auxiliary property:
property type auxProp
onAuxPropChanged: setSomeProp(auxProp)
Which will allow you to use the = operator as well as do bindings.
I recently started using the Durandal library for a SPA I am developing... kudos to the author, it is an excellent library.
I like the concept of widgets, vs using Views for stateless screens, but I was not able to display a widget in a modal dialog, without attaching it to a view. Does anyone know how to do this?
To elaborate, there is a widget.create function that allows for the creation of a widget in the JS but requires a DOM element to attach to. What I would prefer to do is create a widget, without attaching it to the DOM, then call something like:
app.showModal(theWidget);
As an alternative, I know I can create a "dialog" view that maps to swappable widgets, then use that view for dialogs, e.g. the view would have:
<div data-bind="widget: {kind:widgetId}">/div>
... and then:
app.showModal('viewmodels/dialog');
where 'viewmodels/dialog.js' is the view-model for the "Dialog" view.
References:
Modals: http://durandaljs.com/documentation/Showing-Message-Boxes-And-Modals/
Widgets: http://durandaljs.com/documentation/Creating-A-Widget/
Widgets are meant to be reusable controls on a web page, so that's why they require a DOM element. I'm not sure if I completely understand what you're trying to do, but you can define a view that returns its constructor function rather than a singleton object.
Here's a view that returns a singleton:
define([], function() {
var singleton = {
title: "I'm Mr. Singleton"
};
return singleton;
});
Here's the same view, but returns its constructor function:
define([], function() {
var notSingleton = function () {
this.title = "I'm NOT Mr. Singleton"
};
return notSingleton;
});
You can then use either of these within another viewmodel or module, as such:
define(['viewmodels/singleton', 'viewmodels/notSingleton'],
function(singleton, NotSingleton) {
...
app.showModal(singelton);
app.showModal(new NotSingleton());
...
});
In the latter case, you could create multiple dialogs of the same viewmodel across multiple other views, but each would be its own instance with its own properties. If you wanted to share data and/or behaviors across all instances of that viewmodel type, you could add them to the viewmodel's prototype.
Hope this helps.
I have a quite large Flex application with a large set of views and I ceratain views I'd like to add shortcuts.
And i'm looking for something like:
<mx:Vbox>
<foo:Shortcut keys="ctrl+s" action="{bar();}"/>
....
</mx:VBox>
Is there any framwork or component already done that does something like this? I guess it should be too difficult to build? When building this I only want the shortcuts to be active when the view is visible. How do I detect this? What base class is best to inherit from when working with non visual components?
I don't know of any framework component that does that already, but the examples above should get you started if you try to build your own.
There's no need to inherit from any component for a non-visual component like the one you've described here (your "foo" class needs no parents.) There's nothing in the Flex framework you need to inherit from for this.
However you architect it, your foo class is going to have to take in and parse keyboard codes to listen for and accept one or more methods to call. All you have to do is figure out when to add and remove the event listeners that will call the passed-in methods.
To handle turning your keyboard events on and off based on visibility, just have your foo component bind to the "visible" property of it's parent and add/remove event listeners accordingly.
You might also consider having the listeners added when the component that foo is nested in is on the display list rather than just visible. To do this, simply added and remove your event listeners in one of the component lifecycle methods - probably commitProperties is the most appropriate.
I don't think this solution answer your question directly but anyway, to help solve your problem here is an example.
For instance, I've extended the TextArea component like so. This is the best I can do so far, it can definitely be improved upon. Like, I don't know how to make the cursor go to the end after the next shortcut is pressed.
public class TextArea extends mx.controls.TextArea
{
// the keysmap is an example dictionary holding keycodes
private var keysmap:*={
112 = "some text for F1"
,113 = "the text for F2!"
//etc, etc
}
public var handleKeyDown:Boolean =false;
public function TextArea(){
if(handleKeyDown ==true){
this.addEventListener(KeyboardEvent.KEY_DOWN,this.keydownHandler);
}
}
public function keydownHandler(e:KeyboardEvent):void{
if(e.keyCode >= 112 && e.keyCode <= 123){
e.currentTarget["text"] += String(keysmap[e.keyCode]) +" ";
}//focusManager.setFocus(this);
}
}
I can't give you a solution using MXML, however my first thought would involve a singleton static class with a Dictionary that contains a list of objects as its keys and dynamically created dictionaries as the value pairing that contain keys denoting the desired key press with a function reference as the value.
So, say you had a Sprite and you wanted to capture ctrl+s for save when focus is on that object, I would get the instance of that Singleton, and call a function such as registerKeyBinding passing in the Sprite, the keyCode you want, and your pre-defined callback:
private var registeredObjects:Dictionary = new Dictionary(true);
public function registerKeyBinding(targetObject:Object, keyCode:int, callback:Function) {
if (registeredObjects[targetObject]) {
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
}
else {
registeredObjects[targetObject] = new Dictionary();
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
targetObject.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
}
}
private function keyDownListener(e:KeyboardEvent):void {
if (e.ctrlKey == true) {
//calls the function if that key exists.
Dictionary(registeredObjects[e.target])[e.keyCode];
}
}
Can't say I've tested this, but it was just the first thing that popped into my head. You could then setup functions to deregister and delete keys from the dictionaries, check states of the objects in addition to the keyCodes, remove old listeners, and delete entire dictionaries when there is no longer a need for them. Hopefully this is at least a tiny bit helpful.
I'm using a tree control that I want to customize. The data items in the tree's dataProvider have a property name that should be used for labeling the node, and a property type that should be used to select one of several embedded images for use as an icon. The simplest way to do this is by using the labelField and iconFunction properties.
However, I wanted to get started with item renderers and open the door for adding more complex customization later, so I tried making my own item renderer. I extended the TreeItemRenderer class as follows and used it in my tree control:
class DirectoryItemRenderer extends TreeItemRenderer
{
[Embed("assets/directory/DefaultIcon.png")]
private static var _DEFAULT_ICON:Class;
// ... some more icons ...
override public function set data(value:Object):void
{
super.data = value; // let the base class take care of everything I didn't think of
if (value is Node) { // only handle the data if it's our own node class
switch ((value as Node).type) {
// ... some case clauses ...
default:
this._vSetIcon(_DEFAULT_ICON);
}
this.label.text = (value as Node).name;
}
}
private function _vSetIcon(icon:Class):void
{
if (null != this.icon && this.contains(this.icon)) {
this.removeChild(this.icon);
}
this.icon = new icon();
this.addChild(this.icon);
this.invalidateDisplayList();
}
}
This code has no effect whatsoever, icon and label in the tree control remain at their defaults. Using trace(), I verified that my code is actually executed. What did I do wrong?
Looking at the base mx.controls.treeClasses.TreeItemRenderer class, I see that in the updateDisplayList function the renderer gets it's icon and disclosureIcon classes from _listData:TeeListData. Instead of overriding the updateDisplayList function, try modifying the icon and disclosureIcon classes of the renderer's private _listData instance in your _vSetIcon method using the public accessors, like so:
private function _vSetIcon(icon:Class, disclosureIcon:Class = null):void
{
var tmpListData:TreeListData;
if (disclosureIcon == null) disclosureIcon = icon;
tmpListData = this.listData;
tmpListData.icon = icon;
tmpListData.disclosureIcon = disclosureIcon;
this.listData = tmpListData;
}
EDIT
Here is some clarification on the difference between data and listData. You'll have to excuse my omission of package names but I'm editing from my phone so its tough to look them up and I don't know the package names off the top of my head. data is defined in the context of a TreeItemRenderer in the IDataRenderer interface. You create a data renderer by implementing this interface and defining a public property data, which in this case is set by the parent control and contains some data and meta-data from the dataProvider to be rendered by the data renderer class.
listData is defined in the IDropInListItemRenderer interface as a property of type BaseListData and is realized in the TreeItemRenderer class as a property TreeListData. It differs from the data property in that it contains meta-data that describes the TreeListRenderer itself (icon, indent, open) as well as (I believe, I'll have to double check this later) a reference to the data item being rendered. I gather that It's used by the the TreeItemRenderer and I would imagine the parent list control for display update and sizing purposes. Someone is free to correct or add onto that if I'm incorrect or missed something, I'm going of what I remember drom the code.
In this case, you wanted to use meta-data from the data set from the data provider to modify data that determines the display of the renderer, so you would need to modify both.
I think the real confusion here however came from the fact that you extended the TreeItemRenderer class then tried to override functionality on the component in a manner the original developer didn't intend for someone to do, hence the unexpected results. If your goal is education and not ease of implementation you would probably be better served by extending the UIComponent class and using the TreeItemRenderer code as a reference to create a class that implements the same interfaces. That would be a real dive into the pool of custom component development.
I'd probably try something simple, as in this example from the Adobe Cookbooks. I notice that they override updateDisplayList, which may have something to do with your problems.
There's another example (for Flex 2, but looks applicable to Flex 3) that shows how to manage the default icons. It looks like you'll want to manage the icon yourself, setting the default icon styles to null, instead of trying to manipulate the superclass's icon property.
Update -- Looking at the source for TreeItemRenderer, commitProperties has the following before checking the data and setting up the icon and label:
if (icon)
{
removeChild(DisplayObject(icon));
icon = null;
}
Also, it looks like the setter for data calls invalidateProperties. Hence, your icon is wiped out when the framework gets around to calling commitProperties.
I am creating a canvas in actionscript like :
private var cvs_preview:Canvas = null;
private function show_preview():void
{
this.cvs_preview = new Canvas();
this.cvs_preview.id = "cvs_preview_1";
this.cvs_preview.setStyle('backgroundColor', 0x000000);
this.cvs_preview.setStyle('backgroundAlpha', 1);
this.cvs_preview.setStyle('borderColor', 0x417FDD);
this.cvs_preview.setStyle('cornerRadius', 10);
this.cvs_preview.setStyle('borderStyle', 'solid');
this.cvs_preview.setStyle('dropShadowEnabled', true);
var pt:Point = image.localToGlobal(new Point(image.x, image.y));
this.cvs_preview.x = pt.x - 50;
this.cvs_preview.y = pt.y - 50;
this.cvs_preview.height = 200;
this.cvs_preview.width = 250;
//this.cvs_preview.addEventListener(FlexEvent.CREATION_COMPLETE, get_focus_on_canvas);
//this.cvs_preview.focusManager.setFocus(
//this.cvs_preview.addEventListener(MouseEvent.CLICK, end_preview_on_focus_change);
this.cvs_preview.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, end_preview_on_focus_change);
Application.application.addChild(this.cvs_preview); //add as top-most visible container
btn_mini_preview.enabled = false;
}
So on the focus change i want to run the "end_preview_on_focus_change()"
but this is not working.
As per my understanding, i think the canvas not getting any focus in the first place. I was trying to use focusManager.setFocus to do that after the canvas's creation complete. but even that is giving me an error.
the code i was trying on Creation.Complete is :
private function get_focus_on_canvas(e:FlexEvent)
{
focusManager.setFocus(e.target);
//Alert.show("testing img complete");
}
this is giving me an error "1118: Implicit coercion of a value with static type Object to a possibly unrelated type mx.managers:IFocusManagerComponent."
basically i just want to use the focus out event of the canvas.
Can someone help me with this...
I have been on this issue since a long time.
Regards
Zeeshan
The error is correct. You have an object of type Object which you are trying to use as an IFocusManagerComponent. This will not work. To accomplish that line of code, you need to do something like
focusManager.setFocus( IFocusManagerComponent( e.target ) );
This, of course, assumes that the target implements IFocusManagerComponent. It will give you an error otherwise (and likely will in this case because Canvas is not listed as an IFocusManagerComponent). The good news is that Canvas does have a drawFocus method which will accomplish the same thing.
As to your MOUSE_FOCUS_CHANGE event, that will only be fired if an object already HAS focus and then loses it. I think you are better off using FlexEvent.CREATION_COMPLETE. This will ensure that the component has registered itself with all of the appropriate classes in the Flex SDK so that the FocusManager can even be aware of the new object. Whatever you do, do not try to set focus on something which has not been added to the stage (ie: Event.ADDED has been called).
As another piece of advice -- Event.ADDED bubbles, make sure that event.currentTarget == event.target to make sure that you are listening to the correct object. Otherwise, you might be calling the same function multiple times erroneously.
Only a few classes implement IFocusManagerComponent as others mentioned and Canvas is not one of them. If you really must call FocusManager.setFocus() you will have to extend the canvas class to implement this interface and use that class instead. You don't have to write any methods to implement this interface, all methods have already been implemented by UIComponent itself
//FocusableCanvas.as (include appropriate package and import statements)
public class FocusableCanvas extends Canvas implements IFocusManagerComponent
{
public function FocusableCanvas()
{
super();
}
}
//Now use this class instead of Canvas
this.cvs_preview = new FocusableCanvas();
//setFocus in creation complete handler
FocusManager.setFocus(IFocusManagerComponent(e.target));
But if all you want to do is to set focus on the canvas upon it's creation, you can call canvas.setFocus() from the creationComplete handler instead.
private function get_focus_on_canvas(e:FlexEvent)
{
Canvas(e.currentTarget).setFocus();
trace("done");
}
I see two problems, and no perfect solutions. With any luck, this can help you out.
First of all, e.target returns an object typecast with type Object. This explains your implict coercion error, because Object does not implement IFocusManagerComponent.
Second, iFocusManagerComponent is only implemented by Accordion, AdvancedListBase, Button, ButtonBar, ChartBase, ComboBase, DateChooser, DateField, HTML, ListBase, MenuBar, NumericStepper, TabNavigator, TextArea, TextInput, UIMovieClip as per this entry in the Flex 3.4 AS3 Reference.
This leads me to believe that a Canvas element cannot take focus and has simply inherited access to the FocusManager through inheritance of UIComponent.
The only solutions I can see are to utilize something other than Canvas to handle your focus related concerns, or subclass Canvas and implement iFocusManagerComponent, though that looks fairly complex.
Edit
Apologies for missing drawFocus in the above solution.
Please try;
private function get_focus_on_canvas(e:FlexEvent)
{
this.cvs_preview.setFocus();
}