AS3: New instances of my symbol are overwriting properties of previous instances - actionscript-3

So Im working on a project in Flash and I'm having a very strange issue.
The second frame in my project has a series of Actions on it that create multiple copies of a symbol which I've named MessageAction in an array of MessageActions. For some reason, if I create multiple MessageActions, all of the MessageActions on the stage will take on the properties of the most recently made MessageAction, and will act as duplicates of that instance so that anything I do to that instance will also be done to them as well. Specifically I have an OnClick event which "selects" the Message Action, and no matter which one I click, all of them will become selected because they are all mirroring the final one I made.
The code I'm using to create the MessageActions is:
var ActionArray:Array = new Array();
ActionArray[0] = new MessageAction(0);
addChild(ActionArray[0]);
ActionArray[0].x = 73;
ActionArray[0].y = 565;
ActionArray[1] = new MessageAction(1);
addChild(ActionArray[1]);
ActionArray[1].x = 73;
ActionArray[1].y = 615;
ActionArray[2] = new MessageAction(2);
addChild(ActionArray[2]);
ActionArray[2].x = 73;
ActionArray[2].y = 665;
ActionArray[3] = new MessageAction(3);
addChild(ActionArray[3]);
ActionArray[3].x = 533;
ActionArray[3].y = 565;
ActionArray[4] = new MessageAction(4);
addChild(ActionArray[4]);
ActionArray[4].x = 533;
ActionArray[4].y = 615;
ActionArray[5] = new MessageAction(5);
addChild(ActionArray[5]);
ActionArray[5].x = 533;
ActionArray[5].y = 665;
And the Symbol's code is as follows:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class MessageAction extends MovieClip {
//Message Action Values Key:
// 0- Research
// 1- Email Manager
// 2- Get pricing from vendor
// 3- Get pricing from distributor
// 4- Get pricing from partner page
// 5- Quote customer
public static var ActionType: int = -1;
public static var ActionName: Array = new Array("Research", "Email Manager", "Get pricing from vendor", "Get pricing from distributor", "Get pricing from partner page", "Quote Customer");
public static var Selected:Boolean = false;
public static var IsActive:Boolean = true;
public function MessageAction( MyType:int ) {
ActionType = MyType;
this.addEventListener(Event.ENTER_FRAME, onUpdate);
this.addEventListener(MouseEvent.CLICK, clickHandler);
}
public function setType(MyType: int): void {
ActionType = MyType;
}
public function Activate(): void {
IsActive = true;
}
public function Deactivate(): void {
IsActive = false;
}
function clickHandler(me: MouseEvent): void {
if(IsActive == true){
if (Selected == false) {
Selected = true;
} else {
Selected = false;
}
}
}
function onUpdate(e: Event): void {
if (IsActive == true && ActionType != -1) {
alpha = 1;
ActionText.text = ActionName[ActionType];
} else {
ActionText.text = "---";
alpha = .5;
}
if (Selected == false) {
gotoAndStop(1);
} else {
gotoAndStop(2);
}
}
}
}
Any help would be greatly appreciated. Thank you.

Your use of "static" means that the variable is held at the class level, and every instance will share it. Change it in one, and it changes for all. For example, setting Selected to true for one has the effect of setting Selected to true for ALL instances. If you want each instance to behave differently, just remove the "static" keyword. This will cause the variables to be scoped to their own instances.
instead of...
public static var ActionType: int = -1;
public static var ActionName: Array = new Array("Research", "Email Manager", "Get pricing from vendor", "Get pricing from distributor", "Get pricing from partner page", "Quote Customer");
public static var Selected:Boolean = false;
public static var IsActive:Boolean = true;
...try...
public var ActionType: int = -1;
public static var ActionName: Array = new Array("Research", "Email Manager", "Get pricing from vendor", "Get pricing from distributor", "Get pricing from partner page", "Quote Customer");
public var Selected:Boolean = false;
public var IsActive:Boolean = true;

Related

Flex - Cursor search in ArrayCollection not working

So I'm following the book Adobe Flex 4 Training From the Source by Michael Labriola, Jeff Tapper and Matthew Boles, for context.
I'm building a Shopping Cart Class that recieves a ShoppingCartItem object (which is just a POAO) from the mxml and adds it to an ArrayCollection via this public function:
private var $items:ArrayCollection = new ArrayCollection();
public function addItem(item:ShoppingCartItem):void
{
var inCart:Boolean = false;
var currentItem:ShoppingCartItem;
for(var i:int = 0; i < $items.length; i++)
{
currentItem = $items.getItemAt(i) as ShoppingCartItem;
if(item.$product == currentItem.$product)
{
inCart = true;
break;
}
}
if(inCart)
{
currentItem.$quantity++;
}
else
{
$items.addItem(item);
}
updateTotal();
$items.refresh();
}
According to the book, the same function can be achieved with an IViewCursor, like this.
public function addItem(item:ShoppingCartItem):void
{
var cursor:IViewCursor = $items.createCursor();
var inCart:Boolean = cursor.findFirst(item);
if(inCart)
{
var existing:ShoppingCartItem = cursor.current as ShoppingCartItem;
existing.$quantity++;
}
else
{
$items.addItem(item)
}
}
Problem is, when I use this function the item quantity is never updated. Then I have a Shopping cart with 2 entries of 1 product when I should have 1 entry of 2 products. Tracing the inCart boolean yields "false", no matter what I do. The first function works properly and as expected, so I have no idea why the data is not being updated correctly. Also, if I call $items.refresh(); at the end of the second function (for sorting), it throws a NullPointerException error.
Another thing to notice is that I'm using a book for Flex 4 when I'm using the 4.6.0. SDK, the last Adobe release before it was gifted to Apache. I don't know if this is of any importance.
Here's the code for ShoppingCartItem:
[Bindable]
public class ShoppingCartItem
{
public var $product:Product;
public var $quantity:uint;
public var $subtotal:Number;
public function getSubTotal():Number
{
calculateSubTotal();
return $subtotal;
}
public function toString():String
{
return "[ShoppingCartItem]"+$product.prodName+":"+$quantity;
}
public function calculateSubTotal():void
{
this.$subtotal = $product.listPrice*$quantity;
}
public function squeak():void
{
trace("squeak");
}
public function ShoppingCartItem(product:Product, quantity:uint = 1)
{
this.$product = product;
this.$quantity = quantity;
calculateSubTotal();
}
EDIT: More information request by Sunil D.
Product.as class:
[Bindable]
public class Product
{
public var catID:Number;
public var prodName:String;
public var unitID:Number;
public var cost:Number;
public var listPrice:Number;
public var description:String;
public var isOrganic:Boolean;
public var isLowFat:Boolean;
public var imageName:String;
public function toString():String
{
return "[Product]"+this.prodName;
}
public static function buildProductFromAttributes(data:XML):Product
{
var p:Product;
var isOrganic:Boolean = (data.#isOrganic == "Yes");
var isLowFat:Boolean = (data.#isLowFat == "Yes");
p = new Product(data.#catID,
data.#prodName,
data.#unitID,
data.#cost,
data.#listPrice,
data.#description,
isOrganic,
isLowFat,
data.#imageName);
return p;
}
public static function buildProduct(o:Object):Product
{
var p:Product;
p = new Product(o.catId,o.prodName,o.unitID,o.cost,o.listPrice,
o.description,(o.isOrganic == 'true'),
(o.isLowFat == 'true'),o.imageName);
return p;
}
public function Product(cid:Number, name:String, uid:Number, cost:Number, listp:Number, desc:String, iso:Boolean, ilf:Boolean, imn:String)
{
this.catID = cid;
this.prodName = name;
this.unitID = uid;
this.cost = cost;
this.listPrice = listp;
this.description = desc;
this.isOrganic = iso;
this.isLowFat = ilf;
this.imageName = imn;
}
}
ArrayCollection sorting sortfield is the Product POAO contained in the ShoppingCartItem Class. It's done within the constructor function of ShoppingCart like this:
public class ShoppingCart
{
[Bindable]
private var $items:ArrayCollection = new ArrayCollection();
public function ShoppingCart()
{
var prodSort:Sort = new Sort();
var sortField:SortField = new SortField("product");
prodSort.fields =[sortField];
$items.sort = prodSort;
$items.refresh();
}
The reason the viewcursor approach is not working is because the item (ShoppingCartItem) is not in the $items collection. This method compares object references and will hence not find your item if it is another instance of ShoppingCartItem.
In the first approach you are not comparing object references of ShoppingCartItem, but comparing the "product" property of the items.

can i reach a main class variable, by using the value of an external class property

Ok so my problem is this.
In my main class I have a Boolean type variable. In the external class I have a String type variable.
Is it possible to access the variable in my main class, by using the string value of the variable in my external class. Note that the string value of the external class property matches the main class variable.
I just tryed doing this:
Main class CardGame.as has a variable var slot1:Boolean.
In the external class there is the variable var slot:String = slot1;
I also have this line of code: CardGame['slot'] = false;
It doesn't seem to be working :( . Any help would be appreciated. Thanks
Part of the main class file:
function drawCard():void
{
var card:Card = new Card();
if(slot1 == false)
{
card.x = 30;
slot1 = true;
card.slot = "slot1";
}
else if(slot2 == false)
{
card.x = 190;
slot2 = true;
card.slot = "slot2";
}
else if(slot3 == false)
{
card.x = 350;
slot3 = true;
card.slot = "slot3";
}
else if(slot4 == false)
{
card.x = 510;
slot4 = true;
card.slot = "slot4";
}
else if(slot5 == false)
{
card.x = 670;
slot5 = true;
card.slot = "slot5";
}
else
{
card.x = 830;
slot6 = true;
card.slot = "slot6";
}
card.y = cardY;
cardContainer.addChild(card);
}
And the external file:
import flash.display.MovieClip;
import flash.events.MouseEvent;
import CardGame;
public class Card extends MovieClip
{
public var slot:String;
public function Card()
{
// constructor code
addEventListener(MouseEvent.CLICK, removeCard)
}
function removeCard(event:MouseEvent):void
{
this.parent.removeChild(this);
CardGame['slot'] = false;
}
}
You'll need a few lines of code in the external class:
// in CardGame.as
// declare slot1 as a public var right after the class declaration to insure the
// correct scope
public class CardGame extends MovieClip {
public static var slot1:Boolean;
....
// in external class
import CardGame // depends on where this is in relation to the external class
function theFunction():void {
// somewhere in your external class
var slot:String = CardGame.slot1.toString();
}
// Example classes that show how it works
// Main Class - instantiates ExtClass
package {
import flash.display.Sprite;
public class MainVar extends Sprite {
public static var slot1:Boolean;
private var extClass:ExtClass;
public function MainVar() {
slot1 = true;
this.extClass = new ExtClass();
}
}
}
// External Class accesses static var, modifies static var
package {
public class ExtClass {
public function ExtClass() {
var slot:String = MainVar.slot1.toString();
var index:String = "1";
var slotVar:String = "slot" + index;
trace(slot);
// get the value using a string
trace("String access: ", MainVar[slotVar])
MainVar.slot1 = false;
slot = MainVar.slot1.toString();
trace("Var access: ", slot);
// get the value using a string
trace("String access: ", MainVar[slotVar]);
}
}
}

Broadcast Custom Event from Model

`My "Model" is an AS class that extends EventDispatcher:
MeetingInfoModel extends EventDispatcher
In this class I broadcast a custom event:
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
I include a metadata tag at top of class:
[Event(name="updateDocCounter", type="com.fmr.transporter.events.CustomEvent")]
I attempt to listen to for this event in an MXML component:
this.addEventListener( CustomEvent.UPDATE_DOC_COUNTER, onDocUpdate );
But it never reaches this listener.
I've run into issues like this a lot and I think there's a crucial part of the Event process that I'm just not understanding.
Can anyone provide any helpful clues, please?
Thank you!
UPDATE:
In response to all the comments below (thank you for all the responses!):
MeetingInfoModel is not a display component and shouldn't be responsible for broadcasting events; that's the piece I was not getting!!
Here's my code: In the MeetingInfoModel constructor I listen for the collection change event of one of its class members:
docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
In that handler I try to broadcast an event that an MXML component (that is part of the display hierarchy) will handle:
private function updateDocsCounter( event:CollectionEvent ):void
{
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
}
Back in the MXML component, I call this method from the creationComplete handler:
private function addListeners():void{
MeetingInfoModel.getInstance().addEventListener( CustomEvent.UPDATE_DOC_COUNTER, onDocUpdate );
}
It sounds like I should just listen for the collection change event on the MXML component. I tried that but it doesn't work:
MeetingInfo.getInstance().docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
I don't know why that's not working; it seems to be the best solution.
Here's the full MeetingInfoModel class:
[Bindable]
[Event(name="updateDocCounter", type="com.fmr.transporter.events.CustomEvent")]
public final class MeetingInfoModel extends EventDispatcher
{
//Universal INFO
public var generalInfo:GeneralInfoModel;
public var meetingVO:MeetingVO = new MeetingVO();
public var meetingId:String;
public var bulletinBoardLiveMembers:ArrayCollection = new ArrayCollection();
public var xmppServices:XMPPServices;
public var declinedParticipantsGroup:ArrayCollection = new ArrayCollection();
public var notJoinedParticipantsGroup:ArrayCollection = new ArrayCollection();
public var conferenceRoomParticipantsGroup:ArrayCollection = new ArrayCollection();
public var otherLocationParticipantsGroup:ArrayCollection = new ArrayCollection();
[Bindable]
public var documentList:ArrayCollection = new ArrayCollection();
[BIndable]
public var newAttachmentList:ArrayCollection = new ArrayCollection();
public var docsAndAttachmentsList:ArrayCollection = new ArrayCollection();
public var bulletinBoardMsgList:ArrayCollection = new ArrayCollection();
private var _participantList:ArrayCollection = new ArrayCollection();
public var dismissedMeetingIDs:Array = [];
public var visibleToastWindows:Array = [];
public function MeetingInfoModel()
{
generalInfo = GeneralInfoModel.getInstance();
xmppServices = XMPPServices.getInstance();
_participantList.addEventListener(CollectionEvent.COLLECTION_CHANGE, allParticipantsChangeHandler);
bulletinBoardLiveMembers.addEventListener(CollectionEvent.COLLECTION_CHANGE, bulletinBoardLiveMembersChangeHandler);
docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
}
private static var model:MeetingInfoModel = null;
public static function getInstance():MeetingInfoModel
{
if (model == null)
{
model = new MeetingInfoModel();
}
return model;
}
/**
* The handler for the collection change event of the docsAndAttachmentsList collection.
*
* We use it to manually update the counter on the Docs tab.
*/
private function updateDocsCounter( event:CollectionEvent ):void
{
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
}
public function displayToastForThisMeeting(meetingID:Number):Boolean
{
//trace("model::meetingID = " + meetingID);
var doDisplayToast:Boolean = false;
var containsMeetingID:Boolean = false;
//the first one
if(dismissedMeetingIDs.length == 0)
{
//trace("dismissedMeetingIDs.length = 0");
doDisplayToast = true;
dismissedMeetingIDs.push(meetingID);
}
else
{
for(var i:int=0; i < dismissedMeetingIDs.length; i++)
{
//trace("dismissedMeetingIDs[" + i + "] = " + dismissedMeetingIDs[i]);
if(meetingID == dismissedMeetingIDs[i])
{ //this one has already been dismissed
doDisplayToast = false;
containsMeetingID = true;
break;
}
else
{
doDisplayToast = true;
containsMeetingID = false;
}
}
if(containsMeetingID == false)
{
dismissedMeetingIDs.push(meetingID);
}
}
return doDisplayToast;
}
}
Here's some code from my MXML component (whose base class is Group):
import com.fmr.transporter.controller.TransporterController;
import com.fmr.transporter.events.CustomEvent;
import com.fmr.transporter.model.MeetingInfoModel;
import com.fmr.transporter.model.TransporterModel;
import mx.collections.ArrayCollection;
import mx.core.FlexGlobals;
import mx.events.CollectionEvent;
import mx.events.FlexEvent;
private var controller:TransporterController;
[Bindable] public var newAttachmentsList:ArrayCollection;
[Bindable] public var meetingInfo:MeetingInfoModel;
private function complete():void
{
controller = TransporterController.getInstance();
addListeners();
}
/** Add listeners to this class.
*/
private function addListeners():void{
MeetingInfo.getInstance().docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
}
You extended the event class. By default, flex event don't bubble. You need to modify your CustomEvent class constructor to look like this:
public function CustomEvent(type:String){
super(type, true, true);
}
This will make your event bubble and also cancelable per flex event framework. #The_asMan told you correctly about how to deal with a non bubbling event, but I think you may have accidentally missed the right object. Make it bubble and you will hear it!!
#LondonDrugs_MediaServices, #Flextras, and #The_asMan.
These guys are right; it doesn't appear that the class has to be on the display list in order for the event to be listened for. As long as you have the correct target for the class dispatching the event it should be just fine.
For some reason, though, I've done something really weird and couldn't quite figure it out in the end. The solution was to use binding on the collection I updated in the model in order to update the component I wanted.
Thank you everyone for your very helpful comments.

Flex : dynamically created series doesnt show on the chart?

I have the follow class :
package my.controls.charts.series
{
import mx.charts.series.LineSeries;
import mx.collections.ArrayCollection;
import mx.graphics.SolidColorStroke;
import my.controls.charts.ICommonCharts;
public class TimeLineSeries extends LineSeries implements ICommonCharts
{
[Bindable]
protected var dataProviderLineSeries : ArrayCollection;
public var rawData : Array;
public function TimeLineSeries( seriesName : String )
{
super();
this.displayName = seriesName;
this.yField = "value";
this.xField = "dateBegin";
this.sortOnXField = true;
this.filterData = true;
this.setStyle( "form", "segment" );
var stroke : SolidColorStroke = new SolidColorStroke();
stroke.color = 0xFF0000;
stroke.weight = 1;
this.setStyle( "lineStroke", stroke );
rawData = new Array();
dataProviderLineSeries = new ArrayCollection();
this.dataProvider = dataProviderLineSeries;
}
public function Clear() : void
{
rawData = [];
dataProviderLineSeries.removeAll();
}
public function ApplyData() : void
{
dataProviderLineSeries.removeAll();
dataProviderLineSeries = new ArrayCollection( rawData );
dataProviderLineSeries.refresh();
}
}
}
on the application i am trying the follow :
dinamicSeries : Array = new Array();
mySeries : TimeLineSeries = new TimeLineSeries( 'chronos' );
mySeries.rawData = randomData(); // it is a function which gain some random data
mySeries.ApplyData();
dinamicSeries.push( mySeries );
mainChart.series = dinamicSeries;
The new series name appear on the chart, but the data doest, and the chart always remains blank.
- What wrong I am doing ?
Did you affect a vertical axis to your newly created series ?
You need to make public var rawData into a getter/setter pair, so you can populate the ArrayCollection with it. So:
protected var _rawData:Array;
public function get rawData():Array {
return _rawData;
}
public function set rawData(value:Array):void {
if (value != _rawData) {
_rawData = value;
dataProviderLineSeries.source = value;
}
}

undefined method in object?

I get an error when I want to compile the following code...
I get undefined method when I'm trying to set a variable with the Filters class..
trace(filters.txt()); // returns undefined method
trace(filters); // returns [object Filters]
but I'm using this same object in other scripts without problems?
package player {
import flash.display.Sprite;
import filters.Filters;
public class Time_bar extends Sprite {
private var bar = null;
public var color = null;
public var _w = 0;
public var _h = 0;
public var _x = 0;
public var _y = 0;
public function Time_bar(){
this.bar = new Sprite();
addChild(this.bar);
}
public function cnstr(){
this.bar.graphics.beginFill('0x'+this.color);
this.bar.graphics.drawRect(0, 0, this._w, this._h);
this.bar.graphics.endFill();
this.bar.x = this._x;
this.bar.y = this._y;
this.bar.alpha = 0.75;
this.bar.scaleX = 0;
var filters = new Filters();
trace(filters);
trace(filters.txt());
//filters.txt(this.bar);
}
public function progress(float){
this.bar.scaleX = float;
}
}
}
the Filters class looks like this:
package filters {
import flash.display.Sprite;
import filters.Filters_glow;
public class Filters extends Sprite {
private var Glow = new Filters_glow();
public function txt(instance){
Glow.color = '93fafe';
instance.filters = [Glow.filter()];
}
public function loader(instance){
Glow.color = '93fafe';
Glow.alpha = 0.5;
instance.filters = [Glow.filter()];
}
}
}
Filter_glow:
package filters {
import flash.filters.GlowFilter;
public class Filters_glow {
public var color = '000000';
public var alpha = 0.25; // range: 0-1
public var blurX = 4; // range: 0-255; optimized values: 2,4,8,16 etc
public var blurY = 4; // range: 0-255; optimized values: 2,4,8,16 etc
public var strength = 1; // range: 0-255
public var quality = 3; // range: 0-15
public var inner = false;
public var knockout = false;
public function filter(){
this.color = '0x'+this.color;
return new GlowFilter(this.color, this.alpha, this.blurX, this.blurY, this.strength, this.quality, this.inner, this.knockout);
}
}
}
var _filters = new Filters();
_filters.txt(this.bar);
aparently filters is a reserved property name.. after changing filters to _filters the error disapeared :)
To verify whether the method "txt" exists on the filters object you'd have to write trace(filters.txt);. Instead you're executing the txt-method with a missing parameter by adding the brackets. And while executing, it tries to access instance which is undefined. I agree with Matti though that the error message should say something else.
As to why filters.txt(this.bar); doesn't work, I suspect the problem lies elsewhere, can you include the invoking code and the Filters_glow class?
The error is clearly visible. You are not passing the only required argument to the function txt.
public function txt(instance){
Glow.color = '93fafe';
instance.filters = [Glow.filter()];
}
So you can only call txt function by passing it an instance even. something like:
trace(filters.txt(YourObject));
I assume the argument is an instance of the object you want to apply the filter to or something like that. Well it's your function...
try modifying your code as follows, this might help with the error:
//...
public class Filters extends Sprite {
private var Glow: Filters_glow;
public function Filters(){
Glow = new Filters_glow();
}
public function txt(instance: DisplayObject = null): Boolean{
if(!instance){
return false;
}
Glow.color = '93fafe';
instance.filters = [Glow.filter()];
return true;
}
//...