I recently started delving into custom classes in AS3 (to hone my best-practices coding habits), and wanted to create a database class that allows a user to first instantiate a class that contains all the information necessary for methods within the class to add, delete, modify (etc) rows in a MySQL table (via PHP). Of course, this involves using URLRequest, URLLoader and so forth. My question is whether anyone as figured a way how to return data from a method specifically containing that var data without relying upon the method essentially dispatching an event (then having to create a listener rather than having that built into the class). For example,
var myDB:dataBase = new dataBase("dbase","table","username","pword");
//this creates an instance of a database class with methods like:
trace(myDB.fetch(1)); //gets first row of table as a delimited string
OR
if (myDB.delete(1)) {}
//returns Boolean true if delete of row 1 was successful
I found the answer below that contained a way to create a class that returns an event:
Combining URLRequest, URLLoader and Complete Event Listener In Actionscript 3.0?
but I want the method to return a string containing data from the database or a boolean confirmation, not to dispatch an event listener. Here is an example of the class I made:
package com.customClasses {
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLVariables;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequestMethod;
import fl.events.DataChangeEvent;
import flash.events.Event
public class dataBase {
public var dbs:String = "";
public var usr:String = "";
public var pwd:String = "";
public var tab:String = "";
var returnData:String = "";
// Constructor
public function dataBase(dbs:String, usr:String, pwd:String, tab:String) {
this.dbs = dbs;
this.usr = usr;
this.pwd = pwd;
this.tab = tab;
}
public function fetch(idn:uint, par:String):String {
var returnData:String = "blank";
var vUrlReq:URLRequest = new URLRequest ("dBase.php");
var vUrlVars:URLVariables = new URLVariables();
function onLoadVarsComplete(event:Event): void {
//retrieve success variable from our PHP script:
if(event.target.data.msg == "success") {
var rawData:URLVariables = new URLVariables( event.target.data );
returnData = rawData.fromPHP;
} else {
returnData = "failed!";
}
}
vUrlReq.method = URLRequestMethod.POST;
vUrlVars.dir=dbs; // name of table affected
vUrlVars.alpha=usr; // username
vUrlVars.beta=pwd; // password
vUrlVars.dbase=tab; // name of table affected
vUrlVars.func="fetch"; // function for php script to use
vUrlVars.idnum=idn; //if >0 search for record with that id
vUrlReq.data = vUrlVars;
var vLoader:URLLoader = new URLLoader (vUrlReq);
vLoader.addEventListener("complete", onLoadVarsComplete);
vLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
vLoader.load(vUrlReq);
return (returnData);
}
returnData returns "blank"... so I realize my method is not working as intended. I also realize there my be some scope issues with the returnData string, and that I am using a nested function (probably a no-no). Otherwise, any thoughts?
To do what you want, you can use a callback function or a DataEvent listener, like this :
DB.as :
package {
import flash.net.*;
import flash.events.*;
public class DB extends EventDispatcher {
public static const DATA_LOADED = 'data_loaded';
public function DB() {
}
public function getDataUsingDataEvent(file_path:String):void {
var url_loader:URLLoader = new URLLoader();
url_loader.addEventListener(
Event.COMPLETE,
function(e:Event):void
{
var event:DataEvent = new DataEvent(DATA_LOADED, true, false, url_loader.data);
dispatchEvent(event);
}
)
url_loader.load(new URLRequest(file_path));
}
public function getDataUsingCallback(file_path:String, callback:Function):void {
var url_loader:URLLoader = new URLLoader();
url_loader.addEventListener(
Event.COMPLETE,
function(e:Event):void
{
callback(url_loader.data);
}
)
url_loader.load(new URLRequest(file_path));
}
}
}
And then :
var db:DB = new DB();
db.addEventListener(
DB.DATA_LOADED,
function(event:DataEvent):void {
trace(event.data);
}
)
db.getDataUsingDataEvent('file_path');
db.getDataUsingCallback(
'file_path',
function(data:String):void {
trace(data);
}
)
Hope that can help.
As you've stated it, this can't be done in AS3. You cannot wait for an asynchronous operation (such as URLLoader/load()) before returning the function and continuing script execution.
What you can do, if you'd prefer not to use addEventListener() so much, is pass through callbacks, or implement method chaining of promises. These patterns are not necessarily better than using events, and have their own problems, but let you write code that is arguably more readable as a sequence. These patterns are common in Javascript (which has the same asynchronous behavior as ActionScript), for example jQuery. Beware of "callback hell" and "train wrecks". These techniques aim to make things simpler to write but sometimes make things more error prone.
Related
I want to create a login database in Flash via MySQL PHP route. I copied a large portion of the code from some tutorials. My login basically contains users entering their email address picking a password and I have a basic Combobox.
When I run the code I receive this error...
ReferenceError: Error #1069: Property data not found fl.controls.Button and there is no default value.
at phpRegister_fla::MainTimeline/btnHandler()
I have debugged Flash but I don't get any additional information.
After searching online I still don't understand what is causing the error.
I hope my code will help you pinpoint where I am going wrong. Apologies its kind of long.
Any help much appreciated.
import flash.net.URLVariables;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.text.TextField;
import fl.data.DataProvider;
import fl.controls.Button;
btn_Submit.addEventListener(MouseEvent.CLICK, btnHandler);
//Validate form fields
function btnHandler(event:MouseEvent):void {
status_Txt.text = "" + event.target.data.systemResult;
trace(event.target.data.systemResult);
var phpVars:URLVariables = new URLVariables();
var phpFileRequest:URLRequest = new URLRequest("phpFile");
phpFileRequest.method = URLRequestMethod.POST;
phpFileRequest.data = phpVars;
phpVars.email = email.text;
phpVars.ps_wd = ps_wd.text;
var phpLoader:URLLoader = new URLLoader();
phpLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
}
textOneField.addEventListener(Event.CHANGE, changeHandler);
function changeHandler(event:Event):void {
trace("data entered");
}
textTwoField.addEventListener(Event.CHANGE, changeData);
function changeData(event:Event):void {
trace("data changed");
}
email.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
ps_wd.addEventListener(KeyboardEvent.KEY_UP, keyEnter);
function keyHandler(event:KeyboardEvent):void {
if(event.keyCode == Keyboard.ENTER)
trace("keyboard was pressed");
}
function keyEnter(event:KeyboardEvent):void {
if (event.keyCode == Keyboard.ENTER)
trace("Enter button hit");
}
var persons:Array = new Array();
persons[0] = "Male";
persons[1] = "Female";
c_two.dataProvider = new DataProvider(persons);
c_two.addEventListener(Event.CHANGE, dataHandler);
function dataHandler(event:Event):void {
trace(event.target.value);
}
There probably isn't anything wrong with the code. It is rather that you are using a component that doesn't exist in the "standard flash library" namely fl.controls.button. In order for you to be able to use that you need to add a linkage to that component.
Since you didn't mention how you compile your code it is kinda hard to tell you what to do. However, you probably don't need the fl-button but could do with a "SimpleButton" or "Movieclip" or something else instead.
If Flash:
http://forums.adobe.com/message/4260710?tstart=0
Similar issue:
AS3 Error: '1172: Definition fl.controls:Button could not be found.'
More info:
http://www.actionscript-flash-guru.com/blog/14-flcontrols-not-found-how-do-i-import-the-fl-package
In your btnHandler function you have :
... event.target.data.systemResult ...
Where event.target seem to be an fl.control.Button object.
Those objects have no "data" property.
i don't know what you are looking for in event.target.data.systemResult ?
I have a php file that previously used to write xml data with tags. Now I'm trying to make it a little remoteobject based. So instead of writing xml I'm trying to return a class object that consists some big multidimensional array. The problem is it is causing a high latency. I'm not sure if it's my php file that is causing latency problem.
My php code :
class output{
public $grid;
public $week;
public $name;
var $_explicitType = "org.test.output";
}
class manager1{
function init($params,$arrayOut)
{
$action = $params[0];
switch ($action)
{
case "reload": return $this->Reload($arrayOut);break;
default:return $this->form($arrayOut);
}
}
private function Reload($arrayOut)
{
$this->getSlice();
$arrayOut->grid = $this->gridValue();
$arrayOut->week = 'no data';
return $arrayOut;
}
private function form($arrayOut)
{
$arrayOut->grid = $this->gridValue();
$arrayOut->week= $this->getAllWeek($this->ThisYear);
return $arrayOut;
}
}
AS-3 code calling php function:
private function init():void{
var _amf:RemoteObject = new RemoteObject();
var params:Array = new Array(); //parameters array
params.push("default");
var arrayOut:output = new output();//strongly typed class
_amf.destination = "dummyDestination";
_amf.endpoint = "http://insight2.ultralysis.com/Amfhp/Amfphp/"; //amfphp home directory
_amf.source = "manager1"; //the php class which will be called
_amf.addEventListener(ResultEvent.Result, handleResult);
_amf.init(params,arrayOut);
}
private function handleResult(event:ResultEvent):void
{
datagrid.dataProvider = event.result.grid;
}
And there is also a class named output in my application:
package org.test{
public class output
{
public var grid:Array;
public var week:Array;
}
}
I'm using this to pass value to flex remoteobject using amfphp.
Actually, it's fairly easy to figure out.
You can use the Network Monitor that is part of Flash Builder. It shows the Request Time and the Response Time, so you can get a pretty good idea if the issue is with the PHP side or the Flex side. You can also see the size of the response.
Be aware that Remote Objects mixed with Multidimentional arrays can be larger than you think, but again the Network Monitor will help you figure out that.
AS3 code
<fx:Declarations>
<!-- this is the RemoteObject used to make the RPC calls -->
<mx:RemoteObject id="myRemote" destination="MyService" source="MyService"
endpoint="http://localhost/amfphp/gateway.php"
showBusyCursor="true"/>
</fx:Declarations>
protected function button1_clickHandler(event:MouseEvent):void
{
var aut:VOAuthor; // value object class
aut = new VOAuthor();
aut.id_aut = 3;
aut.fname_aut = "test";
aut.lname_aut = "123";
myRemote.saveData(aut);
}
Receving PHP code
public function saveData($author)
{
$mysql = mysql_connect("localhost", "mx112", "xxxxxx");
mysql_select_db("flex360");
$query = "INSERT INTO authors (fname_aut, lname_aut) VALUES ('".$author->fname_aut."', '".$author->lname_aut."')";
$result = mysql_query($query);
return $author;
}
<?php
class VOAuthor {
public $id_aut;
public $fname_aut;
public $lname_aut;
var $_explicitType="org.corlan.VOAuthor";}
?>
Flex network monitor response : Raw view
{lname_aut=123, _explicitType=org.corlan.VOAuthor, fname_aut=test, id_aut=3}
but If I do this at the end of the php code
return $author->lname_aut;
network monitor response is NULL
so the problem is I can print the array but how to cast tht array to a known php type ?
After 5 days I finnaly figured out flex and mysql using amfphp any one please help ?
if you are using amfphp and Flash you have to register your VOs:
import org.corlan.VOAuthor;
// ...
registerClassAlias("org.corlan.VOAuthor", VOAuthor);
only then does php recognize the objects you're sending it from ActionScript.
Yes you need to register your class, and an alternative is to use the metadata tag in the Flex VO declaration.
package VO
{
[RemoteClass(alias="org.corlan.VOAuthor")]
public class VOAuthor
{
private var id_aut : int;
public var fname_aut : String;
public var lname_aut : String;
...
Hope that helps,
Roger.
PS. A more detailed explanation (that helped me) can be found here: http://www.brentknigge.com/?q=node/499
I'm using Actionscript 2.0 in combination with PHP, now I can make a call to my PHP file and receive data but apparently I have to use that data immediately, I cannot use it to fill my class variables.
This is what I want :
class user {
var lastname:String;
function user(in_ID:Number){
var ontvang:LoadVars = new LoadVars();
var zend:LoadVars = new LoadVars();
zend.ID = in_ID;
zend.sendAndLoad("http://localhost/Services/getUser.php", ontvang, "POST");
ontvang.onLoad = function(success:Boolean) {
if (success) {
lastname = ontvang.lastname;
} else {
lastname = 'error';
}
};
}
}
I've found out that this is a big issue in AS2, I found this post to work around it if you're loading XML data but I can't seem to get it to work with LoadVars :
http://www.actionscript.org/forums/showthread.php3?t=144046
Any help would be appreciated ..
When your onLoad handler is called, it is being called as if it were a member function of the LoadVars instance, and not your user instance.
There are several ways around this, one is to use Delegate.create() to create a function which will work as intended, for example:
import mx.utils.Delegate;
class user {
var lastname:String;
var ontvang:LoadVars;
function user(in_ID:Number){
ontvang = new LoadVars();
var zend:LoadVars = new LoadVars();
zend.ID = in_ID;
ontvang.onLoad = Delegate.create(this, onLoad);
zend.sendAndLoad("http://localhost/Services/getUser.php", ontvang, "POST");
};
}
function onLoad(success:Boolean) : Void
{
if (success) {
lastname = ontvang.lastname;
} else {
lastname = 'error';
}
}
}
Don't forget that the load is asynchronous - when you create one of your user objects, the member variables won't be immediately available. What you may need to do is let your user object be capable of signaling its readiness much like LoadVars does, (e.g. with a callback function provided by the caller) so that your app is driven by by these asynchronous events.
There is basic persistence of Javascript vars/etc. You call a function/method, and the next time you call that same function/method, it is holding the data from the last time.
You can delete the vars when you are done with them, but that removes the advantage of using the code again for that instance.
So what is the proper way to write code which can be reused, on different elements, inside the same page.
Therefore, I need the ability to write code so that I can point it at several different elements, and then interact with that code segregated for each element.
So in PHP (as an example) I would do:
$element1 = new MyClass();
$element2 = new MyClass();
$element3 = new MyClass();
in that case it's the same code running in three segregated scopes. How can I do this properly with JS. Even using jQuery's extend() gives me problems.
Thanks.
Use the var keyword when defining local variables (otherwise they'll default to globals).
function foo() {
var i;
// code code code code
}
To create an instance in JavaScript you need to write a constructor function, and call that using new. For instance:
function MyClass( somevalue ) {
this.somevalue = somevalue;
this.somefunction = function() {
alert(somevalue);
}
}
var instance1 = new MyClass(1);
var instance2 = new MyClass(2);
var instance3 = new MyClass(3);
You can namespace your JavaScript to make it a lot like what you're after. See below for an example. It does sound like your problem is related to using global variables where you want to use local variables though - i.e. you declare var myvariable; outside of your function, but only want to use it and forget it within your function. In that case, declare the variable inside your function to make it local.
var MyNameSpace = function() {
return {
sayhello : function() {
alert("hello");
},
saygoodbye : function() {
alert("see ya");
}
};
}();
It sounds like what you're looking for is the ability to have instances of a class and have private data that's associated with each instance.
You can do this using the following technique:
function Foo()
{
// Member variable only visible inside Foo()
var myPrivateVar;
// Function only visible inside Foo()
var myPrivateFunction = function()
{
alert("I'm private!");
}
// Member variable visible to all
this.myPublicVar = "Hi, I'm public!";
// Function visible to all
this.myPublicFunction = function()
{
myPrivateVar = "I can set this here!";
}
}
You can create and use one of these using the following syntax:
var myFoo = new Foo();
myFoo.myPublicVar = "I can set this!";
myFoo.myPublicFunction();