AdvancedDatagrid GroupingCollection in AS3 - apache-flex

My AIR-Application is based on Mate.
I receive Data from a SQLite and put the Date into a ArrayCollection.
In the class of my AdvancedDataGrid, i create a GroupingCollection via mxml. All works fine.
I prefer to build the GroupingCollection in Actionscript. But i can't find anything, how to code this.
In the adobe help itself, they create a GroupingCollection in mxml.
The goal is, to instanciate the gc in model of mate for another class. This will be a chart and the dataProvider must be the gc.
Another Idea is, to build the groupingCollection and put it into the model via two-way-bindung. But I'm not sure, if this will work.
Have you any hint for me?
Thank you
Frank

It works like this. What a fight.
private function onCreationComplete () :void
{
adg.dataProvider = createDataProvider();
}
private function createDataProvider () :GroupingCollection2
{
var tmp:GroupingCollection2 = new GroupingCollection2();
tmp.source = dpArrColl;
tmp.grouping = adgGrouping();
tmp.refresh(false);
return tmp;
}
private function adgGrouping () : Grouping
{
var tmp:Grouping = new Grouping();
tmp.fields = [groupingFieldArray()];
return tmp;
}
private function groupingFieldArray () :GroupingField
{
var tmp:GroupingField = new GroupingField();
tmp.name = "groupName1";
tmp.summaries = [adgSummaries()];
return tmp;
}
private function adgSummaries () : SummaryRow
{
var tmp:SummaryRow = new SummaryRow();
tmp.summaryPlacement = "group";
tmp.fields = [adgSummaryFiled1(), adgSummaryField2()];
return tmp;
}
private function adgSummaryFiled1 () :SummaryField2
{
var tmp:SummaryField2 = new SummaryField2();
tmp.dataField = "Sumfiel1";
tmp.summaryOperation = "SUM";
return tmp;
}
private function adgSummaryField2 () : SummaryField2
{
var tmp:SummaryField2 = new SummaryField2();
tmp.dataField = "Sumfield2";
tmp.summaryOperation = "COUNT";
return tmp;
}
I hope, someone will help this someday.
BR
Frank

Related

Flex applying the sort/filter on an arraycollection without dispatching event

I have a object that is extended from arraycollection. This object has to access and manipulate the arraycollections source object. When this happens, the local sorted/filter copy of data goes out of sync with the source data. To line things up correctly, the sort/filter needs to be re-applied.
To do this normally, you would call refresh() on the arraycollection, but this also broadcasts a refresh event. What I want is to update the sort/filter without dispatching an event.
Having looked into the ArrayCollection class, I can see it is extended from ListCollectionView. The refresh function
public function refresh():Boolean
{
return internalRefresh(true);
}
is in ListCollectionView and it calls this function
private function internalRefresh(dispatch:Boolean):Boolean
{
if (sort || filterFunction != null)
{
try
{
populateLocalIndex();
}
catch(pending:ItemPendingError)
{
pending.addResponder(new ItemResponder(
function(data:Object, token:Object = null):void
{
internalRefresh(dispatch);
},
function(info:Object, token:Object = null):void
{
//no-op
}));
return false;
}
if (filterFunction != null)
{
var tmp:Array = [];
var len:int = localIndex.length;
for (var i:int = 0; i < len; i++)
{
var item:Object = localIndex[i];
if (filterFunction(item))
{
tmp.push(item);
}
}
localIndex = tmp;
}
if (sort)
{
sort.sort(localIndex);
dispatch = true;
}
}
else if (localIndex)
{
localIndex = null;
}
revision++;
pendingUpdates = null;
if (dispatch)
{
var refreshEvent:CollectionEvent =
new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
refreshEvent.kind = CollectionEventKind.REFRESH;
dispatchEvent(refreshEvent);
}
return true;
}
annoyingly, that function is private and so is unavailable to and class that extends ListCollectionView. Also, a lot of what is in the internalRefresh function is private too.
Does anyone know of a way to call internalRefresh from a class that extends ArrayCollection? Or a way of stopping the refresh event from being dispatched when refresh is called?
My (read:hack) solution to this:
addEventListener(CollectionEventKind.REFRESH, handlerHack, true);
The true adds this listener onCapture, before anyone else gets to act on the event.
Before you call the collection.refresh() to update sort/filter, set a boolean flag to true.
discardRefreshEvent = true;
myCol.refresh();
In the listener...
private function handlerHack(evt:CollectionEvent):void
{
if (discardRefreshEvent)
{
evt.stopImmediatePropagation();
discardRefreshEvent = false;
}
}
Disclaimer: Haven't done this exact use before (have implemented similar functionality with other events), also only guessing on Event types/names.
maybe you could extend ArrayCollection, listen to the refresh event and call stopImmediatePropagation() on it when it is fired ? I would start with this...
Good luck :-)

Flex: how to change the volume of an EMBEDDED sound?

Searching for how to change the volume of sounds, I always get this irritating snd=new Sound(URLRequest), followed by snd.setVolume(val). Oh, fine, but my sound is not an URLRequest, it's an embed.
I've made a lot of random attempts (1) to no avail. How am I supposed to do that instead?
(1) including casting my class to Sound, creating a Sound with the embed class as argument, creating a SoundTransform and setting it to the channel, etc.
Instantiate your embedded class like this:
[Embed(source="MySound.mp3")]
public var soundClass:Class;
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
var smallSound:Sound = new soundClass() as Sound;
var trans:SoundTransform = new SoundTransform(.01);
smallSound.play(0,0,trans);
}
Update:
In case what you really wanted to know was how to change the volume if the sound was already playing:
[Embed(source="MySound.mp3")]
public var soundClass:Class;
public var smallSound : Sound;
public var vol : Number = 0.01;
public var trans : SoundTransform;
public var chan : SoundChannel = new SoundChannel();
protected function application1_creationCompleteHandler(event:FlexEvent):void {
smallSound = new soundClass() as Sound;
trans = new SoundTransform(vol);
chan = smallSound.play(0,0,trans);
}
protected function volUp_clickHandler(event:MouseEvent):void {
vol += .1;
trans = new SoundTransform(vol);
chan.soundTransform = trans;
}

as3 duplicating a text field without removing it from stage

I am trying to duplicate a text field. First I get the text with a mc.getChildAt(0) and then copy all the contents into a new textfield. The problem I am having is that getChildAt removes the textfield from the movieclip it is in. How to I get the properties of the textfield without moving it? Or maybe it is something else and what I am doing is fine. Any insight would be a huge help...
var dupeTField:MovieClip = duplicateTextField($value.sourceImg.getChildAt(0));
private function duplicateTextField($textField):MovieClip
{
var currTextField:TextField = $textField;
var dupeTextHolder:MovieClip = new MovieClip();
var dupeTextField:TextField = new TextField();
dupeTextField.text = currTextField.text;
dupeTextField.textColor = currTextField.textColor;
dupeTextField.width = $textField.width;
dupeTextField.height = $textField.height;
dupeTextHolder.addChild(dupeTextField);
return dupeTextHolder;
}
Use something like this:
package com.ad.common {
import flash.text.TextField;
import flash.utils.describeType;
public function cloneTextField(textField:TextField, replace:Boolean = false):TextField {
var clone:TextField = new TextField();
var description:XML = describeType(textField);
for each (var item:XML in description.accessor) {
if (item.#access != 'readonly') {
try {
clone[item.#name] = textField[item.#name];
} catch(error:Error) {
// N/A yet.
}
}
}
clone.defaultTextFormat = textField.getTextFormat();
if (textField.parent && replace) {
textField.parent.addChild(clone);
textField.parent.removeChild(textField);
}
return clone;
}
}
I think you'll find your problem is somewhere else. getChildAt does not remove its target from its parent, and the function you posted works as advertised for me, creating a duplicate clip without affecting the original.
private var dupeTField:MovieClip;
private function init():void
{
//getChildAt will return a DisplayObject so you
//should cast the return DisplayObject as a TextField
var tf:TextField = $value.sourceImg.getChildAt(0) as TextField;
dupeTField = duplicateTextField(tf);
//don't forget to add your duplicate to the Display List
//& make sure to change the x, y properties so that
//it doesn't sit on top of the original
addChild(dupeTField );
}
private function duplicateTextField(textField:TextField):MovieClip
{
var dupeTextHolder:MovieClip = new MovieClip();
var dupeTextField:TextField = new TextField();
//if you pass a TextField as a parameter, you don't need to
//replicate the instance inside the function, simply access the
//parameter directly.
//You may consider copying the TextFormat as well
dupeTextField.defaultTextFormat = textfield.defaultTextFormat;
dupeTextField.text = textField.text;
dupeTextField.textColor = textField.textColor;
dupeTextField.width = textField.width;
dupeTextField.height = textField.height;
dupeTextHolder.addChild(dupeTextField);
return dupeTextHolder;
}

Flex - How do I use a variable to define the name of an instantiated object

Essentially this is what I want to accomplish, however it doesn't work like this. Is there any solution: - The problem is I can't dynamically name a new object... ??? pulling my hair out.
import views.printingView;
public function initComponent(o:Array):void{
SomeObject::Array = o;
for(i=0; i <=SomeObject.length-1; i++){
'invoice'+{SomeObject[i].namedID}:printingView = new printingView();
someDisplayContainer.addChild('invoice'+{SomeObject[i].namedID});
'invoice'+{SomeObject.namedID}.publicInitFunction(SomeObject[i]);
}
}
From the code you posted, there's no need for a dynamically named variable at all. The same code can be simplified to this:
import views.printingView;
public function initComponent(o:Array):void{
for each(var item:Object in o)
{
var v:printingView = new printingView();
someDisplayContainer.addChild(v);
v.publicInitFunction(item);
}
}
If for some reason you really need a dynamically named variable, you can do it like this. This assumes the current object is declared dynamic.
import views.printingView;
public function initComponent(o:Array):void{
SomeObject::Array = o;
for each(var item:Object in o)
{
var name:String = 'invoice' + item.namedID;
this[name] = new printingView();
someDisplayContainer.addChild(this[name]);
this[name].publicInitFunction(item);
}
}

Best way to typecast deserialised JSON

I think I've established that in as3corelib JSON.decode I have no choice but to deserialise to a plain old flex object.
var data:Object = JSON.decode(json);
If I then want to get the data contained in the object into another type I can't use type casting. I have to instantiate a new instance and add the properties manually.
var data:Object = JSON.decode(json);
var model:Model = new Model();
model.name = data.name;
model.notes = data.notes;
A pain and a bit ugly, but I'm guessing this is the price to be paid for going from untyped json to a flex type. My first question is whether my assumption is correct and there is no prettier way to create my model instance with the data contained within the json?
My second question, if so then before I write my own method to do this, is there anything inside the flex api that will take the data object and mixin it's values to my model instance?
Cheers,
Chris
the approach I've always used proved to be part of the AMF3 serialization mechanism in ActionScript.
have a look at IExternalizable and registerClassAlias.
now what I use is the following:
interface ISerializable {
public function getRawData():Object;
public function setRawData(param:Object):void;
}
function registerType(id:String, type:Class):void {
//implementation
}
function getTypeByID(id:String):Class {
//implementation
}
function getTypeID(type:Class):String {
//implementation
}
and to the decoder/encoder you register a class alias.
serialization of an object works as follows:
var raw:Object = model.getRawData();
raw[" type"] = getTypeID(model);
var encoded:String = JSON.encode(raw);
decoding works as follows:
var raw:Object = JSON.decode(raw);
var cl:Class = getTypeByID(raw[" type"]);
if (cl == null) throw new Error("no class registered for type: "+raw[" type"]);
delete raw[" type"];
var model:ISerializable = new cl();
model.setRawData(raw);
you will need to do this recursively on the whole deserialized JSON tree, starting at the leafs.
For cyclic reference, you'll need a trick.
I had an implementation of this somewhere, but I can't find it.
You can loop within the field of you json decoded object and assign them into your model:
function json2model(json:String):Model{
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
var model:Model=json2model(json)
or add a static function within your Model if you preffer:
public class Model {
//...
public static function fromJSon(json:String):Model {
var data:Object = JSON.decode(json);
var m:Model=new Model();
for (var field:String in data) {
if (m.hasOwnProperty(field)) {
m[field] = data[field];
}
}
return m;
}
}
}
var model:Model=Model.fromJSon(json);

Resources