How to access mxml (Flex) DOM elements? - apache-flex

Is there a way to access the DOM-Elements of a mxml file in a way that you can in JS (e.g. using Prototype or jQuery)?
I need to know if a top-level element has a child (sub-sub-...-childs) with a certain id.
In JS (using prototype) it would be something like:
$('tabs').select('[id="something"]');
Any ideas?

Depending on what you're trying to do, Bifff might be the answer. Think of it as "JQuery for Flex": https://github.com/seanhess/bifff/wiki

You can recursively search through the structure. Something like this (Might not be the most efficient in your case):
private function hasChild(node:UIComponent, target:String):Boolean
{
if(node.id == target)
{
return true;
}
else
{
var hasTarget:Boolean = false;
for(var i:int = 0; i < node.numChildren; i++)
{
hasTarget = hasTarget || hasChild(node.getChildAt(i));
}
return hasTarget;
}
}

Related

How do I query for the existance of a property without knowing its path

Is there a way to query cosmos DB for the existence of a property within a document without knowing where exactly this property is located as it might appear in different locations?
Basically
SELECT *
FROM SomeCollection
WHERE IS_DEFINED("here should be just the prop name w/o any path")
Edit:
I initially missed to state that I was looking for a solution on the query level instead of writing a user defined stored procedure
you can create UDF which will recursively navigate though all object properties and return true if found on any level. In your case udf body can look similar to this
function findRecursive(theObject, searchingProperty){
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
if (findRecursive(theObject[i]){
return true;
}
}
}
else
{
for(var prop in theObject) {
if(prop == searchingProperty) {
return true;
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array){
if (findRecursive(theObject[prop])){
return true;
}
}
}
}
return false;
}
UDF should be register same as stored procedures. And can be called from queries.
So your select will look like next
SELECT *
FROM root
WHERE udf.findRecursive(root, "here should be just the prop name w/o any path")
P.S. I didn't test the code, but you should get the basic idea

Iterating over ArrayCollection while adding and removing items

I want to iterate over an ArrayCollection in Flex while there can be items added and removed.
Since i didn't find a way to use a "classic" Iterator like in Java, which would do the job. I tried the Cursor. But it doesn't really work the way i want it to be ;) So how do I do it nicely ?
var cursor:IViewCursor = workingStack.createCursor();
while (!cursor.afterLast)
{
// Search
deepFirstSearchModified(cursor.current.node,nodeB,cursor.current.way);
// Delete Node
cursor.remove();
// Next Node
cursor.moveNext();
}
I think better to use New Collection/Array for opertations as
private function parseSelectedItem(value:IListViewCollection):IListViewCollection{
var result:Array = new Array();
for each(var item:Object in value)
{
//result.push();
//result.pop();
}
return new ArrayCollection(result) ;
}
Hopes that helps
Try to use the following:
for (var i:int = myArrayCollection.length - 1; i >= 0; i--) {
myArrayCollection.removeItemAt(i);
}
There is a solution for your problem:
http://www.ericfeminella.com/blog/actionscript-3-apis/
Have a look at the CollectionIterator class.
Cheers
Take a look at ActionLinq. It implements the .Net Linq2Objects pattern, including IEnumerable. Of course, you need to be careful, because you are modifying the items you are iterating over...
var workingStack:ArrayCollection = getData();
var iterator:IEnumerable = Enumerable.from(workingStack);
for each(var item:String in iterator) {
doSomethingTo(workingStack);
}
In flex (or actionscript) any change that you do, is visible instantly. So you can do what you want in a for:
for (var i : Number = myArrayCollection.length; i > 0; i--) {
myArrayCollection.removeItemAt(i - 1);
}
I think that should work fine.

how could I traverse through children of a UIComponent Class in flex

I am dynamically creating a UI component based on XML templates stored in database. Now I want to access one of the child component of this UIComponent Class. I dont see any children property in this class? How could I possibly traverse through each child comppnent of this class and set its properties according to some logic in code(which I could not do in templates itself).
Basically what I want to do is something like this:
var cover:UICoomponent = generateScreen()
for each(var obj:UIComponent in cover.children){
if (obj.id =="myComponent"){
obj.SomProperty = "SomeValue";
}
}
Any help will be greatly appreciated. Thanks.
Use numChildren:
var cover:UICoomponent = generateScreen()
for (var i:int = 0; i < cover.numChildren; i++)
{
var obj:UIComposer = cover.getChildAt(i) as UIComponent;
if (obj.id =="myComponent") {
obj.SomProperty = "SomeValue";
}
}
Update: Traverse recursively
function findChild(childId:String,container:DisplayObjectContainer=null):UIComponent
{
container ||= this;
for (var i:int = 0; i < container.numChildren; i++)
{
var obj:UIComponent = container.getChildAt(i) as UIComponent;
if (obj.id == childId)
return obj;
if (obj is DisplayObjectContainer)
{
// search for children
child = findChild(childId,obj as DispalyObjectContainer);
if (child)
return child;
}
}
}
Usage:
var child:UIComponent = findChild("myComponent");
child.someProperty="SomeValue";
I haven't compiled this - but you get the general idea.

How to assign custom CSS class to arbitrary arbitrary rows of h:dataTable?

I'm trying to assign a specific CSS class to specific rows of my <h:dataTable>. Is there some way to access and cutomize the resulting table rows?
Bind the rowClasses attribute to a bean property which returns the desired string of CSS classes.
<h:dataTable value="#{bean.list}" rowClasses="#{bean.rowClasses}">
with e.g.
public String getRowClasses() {
StringBuilder rowClasses = new StringBuilder();
for (Item item : list) {
if (rowClasses.length() > 0) rowClasses.append(",");
rowClasses.append(item.getRowClass());
}
return rowClasses.toString();
}
Update to clarify, this way you have full programmatic control over the rowClasses string. Note that the above is just a kickoff example, it doesn't necessarily need to be obtained by Item#getRowClass() or so. You can even do it in a simple for loop with a counter.
E.g.
public String getRowClasses() {
StringBuilder rowClasses = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (rowClasses.length() > 0) rowClasses.append(",");
rowClasses.append(selected.contains(i) ? "selected" : "none");
}
return rowClasses.toString();
}
where selected is a List<Integer>. If it contains 1, 2 and 5, then the returned string will look like as follows for a list of 10 items:
none,selected,selected,none,none,selected,none,none,none,none
I like #BalusC suggestion. If you want a second alternative, you can do this easily with javascript/JQuery.
With JQuery you can do it like this
(Note, this is just an example. I haven't tested it, and there is probably a better way of doing it)
$(document).ready(function(){
var counter = 0;
$('#myTable').each(function() {
counter = counter + 1;
if(counter==3) {
$(this).addClass('redRow');
return;
}
});
}

How to create custom MouseEvent.CLICK event in AS3 (pass parameters to function)?

This question doesn't relate only to MouseEvent.CLICK event type but to all event types that already exist in AS3. I read a lot about custom events but until now I couldn't figure it out how to do what I want to do. I'm going to try to explain, I hope you understand:
Here is a illustration of my situation:
for(var i:Number; i < 10; i++){
var someVar = i;
myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}
function doSomething(e:MouseEvent){ /* */ }
But I want to be able to pass someVar as a parameter to doSomething. So I tried this:
for(var i:Number; i < 10; i++){
var someVar = i;
myClips[i].addEventListener(MouseEvent.CLICK, function(){
doSomething(someVar);
});
}
function doSomething(index){ trace(index); }
This kind of works but not as I expect. Due to the function closures, when the MouseEvent.CLICK events are actually fired the for loop is already over and someVar is holding the last value, the number 9 in the example. So every click in each movie clip will call doSomething passing 9 as the parameter. And it's not what I want.
I thought that creating a custom event should work, but then I couldn't find a way to fire a custom event when the MouseEvent.CLICK event is fired and pass the parameter to it. Now I don't know if it is the right answer.
What should I do and how?
You really need to extend the event class to create your own event with extra parameters. Placing functions inside the addEventListener (anonymous functions) is a recipe for memory leaks, which is not good.
Take a look at the following.
import flash.events.Event;
//custom event class to enable the passing of strings along with the actual event
public class TestEvent extends Event
{
public static const TYPE1 :String = "type1";
public static const TYPE2 :String = "type2";
public static const TYPE3 :String = "type3";
public var parameterText:String;
public function TestEvent (type:String, searchText:String)
{
this.parameterText = searchText;
super(type);
}
}
when you create a new event such as
dispatchEvent(new TestEvent(TestEvent.TYPE1, 'thisIsTheParameterText'))" ;
you can then listen for that event like this
someComponent.addEventListener(TestEvent.TYPE1, listenerFunction, true , 0, true);
and inside the function 'listenerFunction' event.parameterText will contain your parameter.
so inside your myClips component you would fire off the custom event and listen for that event and not the Click event.
Without knowing more about your application, it seems more like you should use the target to pass parameters, or extend MouseEvent. The former would be more in line with common practice, though. So for example, if you exposed an integer public property on your "clip" object (whatever it is):
public class MyClip
{
public var myPublicProperty:int;
public function MyClip() { //... }
}
for (var i:int = 0; i < 10; i++)
{
myClips[i].myPublicProperty = i;
myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}
... and then, in your event listener, you could retrieve that property using either the target or currentTarget property of the event (probably currentTarget, in your case):
function doSomething(event:MouseEvent):void
{
trace(event.currentTarget.myPublicProperty.toString());
}
That ought to do it! Good luck.
private function myCallbackFunction(e:Event, parameter:String):void
{
//voila, here's your parameter
}
private function addArguments(method:Function, additionalArguments:Array):Function
{
return function(event:Event):void {method.apply(null, [event].concat(additionalArguments));}
}
var parameter:String = "A sentence I want to pass along";
movieClip.addEventListener(Event.COMPLETE, addArguments(myCallbackFunction, [parameter] ) );
Take advantage of the dynamic function construction in AS3.
You can accomplish this by getting your handler out of a function that gives the variable closure, like this:
for (var i=0; i<5; i++) {
myClips[i].addEventListener( MouseEvent.CLICK, getHandler(i) );
}
function getHandler(i) {
return function( e:MouseEvent ) {
test(i);
}
}
function test( j ) {
trace("foo "+j);
}
Also, as for why this creates a new closure, you might want to check the explanation in the accepted answer to this similar question.
Thanks so much for this usefull tips, this technique is better to understand than classes explanation.
for me I just started new code algorithm using this technique to solve link relation between timers array and viewports array, and update status by change text inside them frequently, by passing ID's with timers events.
like this:
var view:Object=[];
for(var i:uint=0;i<Camera.names.length;i++){
view[i]=getChildByName("Cam"+i);
//_________ Add Text _________
var tf:TextField = new TextField();
tf.autoSize = TextFieldAutoSize.LEFT;
tf.textColor=0xffffff;
view[i].tf=view[i].addChild(tf);
//_________ Add Timer _________
var t:Timer = new Timer(1000);
view[i].timer=t;
view[i].timer.start();
view[i].timer.addEventListener(TimerEvent.TIMER, addArg(i));
}
function addArg(adi:uint):Function {
return function(event:TimerEvent):void {
updatecamstatus(adi);
}
}
function updatecamstatus(vH:uint):void {
with (view[vH]){
tf.text="Cam" + vH + "\n";
tf.appendText("activityLevel: " + videosource.activityLevel + "\n");
tf.appendText("motionLevel: " + videosource.motionLevel + "\n");
}
}
I see your main goal isn't actually to create a custom MouseEvent.CLICK, but to pass a parameter to the function. You don't need to complicatedly create or extend anything. There's a simple and closure-trouble-free way to do it.
Just make your function like this:
function doSomething(index:Number):Function {
return function(e:MouseEvent):void {
// Use "e" and "index" here. They'll be unique for each addEventListener()
trace(index);
}
}
This technique can relate to any AS3 event type you can use addEventListener on.
And now you can add it like this:
var functionsDoSomething:Object;
for (var i:Number = 0; i < 10; i++) {
var someVar:Number = i;
functionsDoSomething[i] = doSomething(someVar);
myClips[i].addEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}
The doSomething(someVar) can be used directly on addEventListener(), but it's better to keep it in a variable because you'll be able to remove it later the same fashion you added it:
for (i = 0; i < 10; i++) {
myClips[i].removeEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}
The commonly used e.currentTarget.someCustomProperty works for dynamic objects (i.e. MovieClip), but will let you down at anything else (i.e. Sprite), forcing you to build a whole custom extended object/event for every type.
This solution deals with every "listenable" object and event. And this answer has more details and examples on it.

Resources