Flex - Cursor search in ArrayCollection not working - actionscript-3

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.

Related

Flex - Problems in accessing static variable on another mxml page

First.mxml - Contains a Datefield control as follows:
<mx:DateField id="G2_CRTLoadDate" width="150" selectedDate="{modelProxy.G2_CRTLoadDate}" change="{modelProxy.G2_CRTLoadDate = event.currentTarget.selectedDate;changeManagerStatus()}"/>
I'm assigning this Datefield value to a static variable CERT_LOAD_DATE as follows(First.mxml):
[Bindable]
public static var CERT_LOAD_DATE:String = "";
private function changeManagerStatus():void
{
CERT_LOAD_DATE = G2_CRTLoadDate.selectedDate.toDateString();
}
Second.mxml -Here, I have a Combobox as follows:
<mx:ComboBox id="General_Release_Dates"
selectedItem="{modelProxy.General_Release_Dates}"
valueCommit="{model.General_Release_Dates = event.currentTarget.selectedItem;updateReleaseDate(event)}"
change="{model.General_Release_Dates = event.currentTarget.selectedItem;updateReleaseDate(event)}" close="closeHandler(event);" includeInLayout="true" visible="true">
</mx:ComboBox>
Inside the closeHandler function, I'm trying to access the variable CERT_LOAD_DATE as follows:
private function closeHandler(evt:DropdownEvent):void {
var CurrentDate:Date = new Date();
if(General_Release_Dates.selectedLabel.toString() == "TBD")
{
Alert.show(First.CERT_LOAD_DATE);
}
}
The alert box displays no value (null). Please help.
I can't figure out the relationship between First.mxml and Second.mxml from your question.
However, the following code can't access First.mxml.
Alert.show(First.CERT_LOAD_DATE);
Because the "First" is not the same instance as loaded "First.mxml".
How about to use singleton? It's accessible from anywhere.
1st, add MySingleton.as class like this.
package foo.bar
{
public class MySingleton
{
private var _cert_load_date:String;
public function MySingleton(internally:SingletonInternal)
{
super();
if(internally == null)
{
throw new Error("Please use getInstance() method.");
}
}
public static function getInstance():MySingleton
{
return SingletonInternal.instance;
}
public function set cert_load_date(value:String):void
{
_cert_load_date = value;
}
public function get cert_load_date():String
{
return _cert_load_date;
}
}
}
import foo.bar.MySingleton;
class SingletonInternal{
public static var instance:MySingleton
= new MySingleton(new SingletonInternal());
public function SingletonInternal(){}
}
How to use
Set value at First.mxml.
public var singleton: MySingleton = MySingleton.getInstance();
private function changeManagerStatus():void
{
singleton.cert_load_date = G2_CRTLoadDate.selectedDate.toDateString();
}
Second.mxml
public var singleton: MySingleton = MySingleton.getInstance();
private function closeHandler(evt:DropdownEvent):void {
var CurrentDate:Date = new Date();
if(General_Release_Dates.selectedLabel.toString() == "TBD")
{
Alert.show(singleton.cert_load_date);
}
}
Updated: Aug 27 10:00(JST)
I think there are two way to change First.mxml's element using singleton.
1) Binding the DateField value to singleton variables, and clear the value at Secend.mxml.
2) Assign to singleton variables whole "First", and control from Second.mxml.
I'll write here the 2nd way.
If you use this way, anything is controlable from Second.mxml.
MySingleton.as
private var _first:Object;
public function set first(value:Object):void
{
_first = value;
}
public function get first():Object
{
return _first;
}
First.mxml
singleton.first = this;
Second.mxml
public function something(): void{
First(singleton.first).G2_CRTLoadDate.selectedDate = null;
// The cast is unnecessary. Following code also works.
// singleton.first.G2_CRTLoadDate.selectedDate = null;
}
Also you can execute First.mxml's public function from Second.mxml.
singleton.first.someFunctionDefinedAtFirst();

as3 use setter in other class(parent)

I'm trying to make a simple setter which i like to access with a child class.
Here is the parent class code :
public class DeGeheimeMissie extends MovieClip
{
var deLader:URLLoader;
var deXmlData:XML;
public var hoofdVragenLijst:Object = new Object();
public var subVragenLijst:Object = new Object();
public var hetAntwoord:String;
public function DeGeheimeMissie()
{
var hetVerzoek:URLRequest;
hetVerzoek = new URLRequest("dialogen.xml");
deLader = new URLLoader(hetVerzoek);
deLader.addEventListener(Event.COMPLETE, vragenGeladen);
deLader.load(hetVerzoek);
instObama.addEventListener(MouseEvent.CLICK, showVragen);
instObama.buttonMode = true;
instObama.useHandCursor = true;
}
public function set hetAntw(str:String):void
{
hetAntwoord = str;
}
and here is the child class where i try to access it:
public class Rutte extends MovieClip
{
var deLader:URLLoader;
var deXmlData:XML;
public var antwoordenOverzicht = [];
//constructor
public function Rutte()
{
var hetVerzoek:URLRequest;
hetVerzoek = new URLRequest("dialogen.xml");
deLader = new URLLoader(hetVerzoek);
deLader.addEventListener(Event.COMPLETE, antwoordenGeladen);
deLader.load(hetVerzoek);
instRutte.addEventListener(MouseEvent.CLICK, showAntwoorden);
instRutte.buttonMode = true;
instRutte.useHandCursor = true;
}
public function setAntwoord(e:MouseEvent)
{
var antw = e.currentTarget.text.charAt(0);
trace(e.currentTarget.text.charAt(0));
this.parent.hetAntw(antw);
}
The error i get here is:
1061: Call to a possibly undefined method hetAntwoord through a reference with static type flash.display:DisplayObjectContainer.
You should try this, this is how we use setters :
this.parent.hetAntw = antw;
But why making a public setter on a public static var ?

AS3 Can't get array from another class

Well, I have a problem, with searching changes of array in ItemProperty class. When i calling to trace item array length, than it gets 0. But in ItemProperty class only in RecieveIndex function item length getting more than 2. Even ReturnData() got 0. The question is how to get final result of this array(Item) in InventoryData class?
That is class, when i got 0:
public class InventoryData extends Sprite {
private var item:ItemProperty = new ItemProperty();
public function InventoryData(){
trace(item.Item);
trace(item.Item.length);
//trace(item.ReturnData());
}
}
Class where array was formed. In RecieveIndex method Item length more than 0.
public class ItemProperty extends Sprite{
public var Item:Array = [];
public function ItemProperty() {
var connect:Connection = new Connection();
connect.setRequest("Select", "`inventory`", ["all"], ["all"], InventoryContent);
}
private function InventoryContent(RecieveData:Array):void
{
var picking:Connection = new Connection();
picking.setRequest("Select","`weapon`",["all"],["id IN("+RecieveData[0].contents.toString() + ")"], InventotyData);
}
private function InventotyData(RecieveData:Array):void{
var indexdecode:Connection = new Connection();
for(var i:uint=0; i<RecieveData.length; i++)
{
indexdecode.loadImageFromBase64(RecieveData[i].id, RecieveIndex);
}
}
public function RecieveIndex(index:int):void {Item.push(index); }
public function ReturnData():Array {return Item;}
}
Something like this with the event I mentioned in my comment:
public class InventoryData extends Sprite {
private var item:ItemProperty = new ItemProperty();
public function InventoryData(){
item.addEventListener(ItemProperty.ARRAY_READY, onArrayReady);
}
private function onArrayReady(e:Event):void {
trace(item.Item.length);
}
}
In ItemProperty class you declare a new constant:
public static const ARRAY_READY:String = "arrayReady";
and once you have the array filled with the items you want to retrieve, you add:
dispatchEvent(new Event(ARRAY_READY));

AS3 TypedDictionary?

public dynamic class TypedDictionary extends Dictionary {
private var _type:Class;
public function TypedDictionary(type:Class, weakKeys:Boolean = false) {
_type = type;
super(weakKeys);
}
public function addValue(key:Object, value:_type):void {
this[key] = value;
}
public function getValue(key:Object):_type{
return this[key];
}
...
I want to make TypedDictionary with typisation. But I can not use _type in addValue and getValue. The idea is to use next construction :
var td:TypedDictionary = new TypedDictionary(myClass, true);
td.addValue("first", new myClass());
...
var item:myClass = td.getValue("first");
item.someFunction();
Is any ability to use dynamic class type?
If I un derstand what you are asking for.
This is untested code so it could be error prone, but it should get you on the right path.
public dynamic class TypedDictionary extends Dictionary {
private var _type:Class;
public function TypedDictionary(type:String, weakKeys:Boolean = false) {
_type = getDefinitionByName(type) as Class;
super(weakKeys);
}
public function addValue(key:Object, value:*):void {
if(_type == getDefinitionByName(getQualifiedClassName(value))){
this[key] = value;
}else{
trace('failed type match')
}
}
public function getValue(key:Object):*{
return this[key];
}
...
var td:TypedDictionary = new TypedDictionary("com.somepackage.myClass", true);
td.addValue("first", new myClass());
var item:myClass = td.getValue("first");
item.someFunction();

In ActionScript 3, tracing all properties of a value object

I have a number of value objects and I want to be able to create a function within it to trace out the properties and the values without specifying them directly. It will allow me to use the same code in all value objects as opposed to referring to variables within the value object. Ideally, I would like to replace the code in blogURLVars with this code.
Here's a sample value object
package items {
public class Blog {
private var _itemID:uint;
private var _blogTitle:String;
private var _blogText:String;
private var _blogCreated:String;
private var _blogCategory:String;
private var _blogFrontpage:Boolean;
public function Blog (itemID:uint,blogTitle:String,blogText:String,blogCategory:String,blogCreated:String,blogFrontpage:Boolean=false) {
_itemID = itemID;
_blogTitle = blogTitle;
_blogText = blogText;
_blogCreated = blogCreated;
_blogCategory = blogCategory;
_blogFrontpage = blogFrontpage;
}
public function get itemID():uint {
return _itemID;
}
public function get blogTitle():String {
return _blogTitle;
}
public function get blogText():String {
return _blogText;
}
public function get blogCategory():String {
return _blogCategory;
}
public function get blogCreated():String {
return _blogCreated;
}
public function get blogFrontpage():Boolean {
return _blogFrontpage;
}
public function toString():void {
}
public function blogURLVars():String {
var s:String;
s = "itemID="+ _itemID;
s += "blogTitle="+ _blogTitle;
s += "blogText="+ _blogText;
s += "blogCategory="+ _blogCategory;
s += "blogCreated="+ _blogCreated;
s += "blogFrontpage="+ _blogFrontpage;
return s;
}
}
}
DescrybeType could be of help here. I'm basing this answer in this other answer (you might want to check it out): Fastest way to get an Objects values in as3
Basically, the describeType function will let you inspect the public interface of your object. That means public variables, getter/setters and methods (plus some other info not relevant to your problem). So you get a list of all the getters and with that, return the names of said properties plus their actual values for a given object. Not that this is not exactly the same as your sample code, since this will not use the private variables, but rather will call the getters.
In code, this could be something like this (based on code in the linked question).
package {
import flash.net.URLVariables;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
public class PropertiesHelper {
public function PropertiesHelper() {
}
private static var typePropertiesCache:Object = {};
public static function getPropertyNames(instance:Object):Array {
var className:String = getQualifiedClassName(instance);
if(typePropertiesCache[className]) {
return typePropertiesCache[className];
}
var typeDef:XML = describeType(instance);
trace(typeDef);
var props:Array = [];
for each(var prop:XML in typeDef.accessor.(#access == "readwrite" || #access == "readonly")) {
props.push(prop.#name);
}
return typePropertiesCache[className] = props;
}
public static function getNameValuePairs(instance:Object):URLVariables {
var props:Array = getPropertyNames(instance);
var vars:URLVariables = new URLVariables();
for each(var prop:String in props) {
vars[prop] = instance[prop];
}
return vars;
}
}
}
Use:
var blog:Blog = new Blog(1,"title&","text","cat","created");
var urlVars:URLVariables = PropertiesHelper.getNameValuePairs(blog);
trace(urlVars);
I'm using a URLVariables object since it seems that's what you want (though not actually what you blogURLVars method does), but you could change that in the getNameValuePairs method to suit your needs if necessary. An advantage of using a URLVariables object is that it handles the url-encoding for you automatically, so reserved characters such as &, =, etc, should not be a problem.