Google Map หยุดพัฒนา ActionScript API

นักพัฒนาจากทาง Google ได้ประกาศออกมาแล้วว่าจะไม่มีการพัฒนา ActionScript API แล้ว ซึ่งทางบทความนี้ได้เผยแพร่ในบล็อก เมื่อวันที่ 2 ก.ย. ที่ผ่านมา

http://googlegeodevelopers.blogspot.com/2011/09/maps-api-for-flash-deprecation.html

นั่นหมายความว่าฟีเจอร์หรือความสามารถใหม่ๆ ที่มีอยู่หรือที่จะออกมาในอนาคต จะไม่สามารถเรียกใช้งานได้จากแอพลิเคชั่นที่สร้างจาก Flash Plateform ได้อีกต่อไป แต่ทาง Google ก็ได้เสนอทางเลือกใหม่ๆ ให้ใช้ JavaScript Map API v3 แทน โดยเหตุผลว่าทราฟฟิคที่เรียกใช้งานจาก Flash Plateform Application น้อยในขณะที่การใช้งานโดย JavaScript Map Application มีมากขึ้นเรื่อยๆ ซึ่งคงจะดีกว่าหากมุ่งเน้นพัฒนาให้รองรับกับความต้องการตรงนี้

อย่างไรก็ตามยังมีนักพัฒนาบางท่านได้สอบถาม Google ว่าถ้าไม่พัฒนาก็อยากให้ Open Source Flash API เสียเลย อาจจะเป็นประโยชน์ต่อนักพัฒนาที่อยากนำไปต่อยอด แต่ทาง Google ก็ยังไม่มีข้อสรุปว่าจะทำหรือไม่

รายละเอียดเพิ่มเติม http://code.google.com/apis/maps/documentation/flash/

การพิจารณาเลือก ActionScript Framework เพื่อพัฒนา Flex App

ขอยาวหน่อย
ผมเคยใช้ MVC คือ Cairngorm
แล้วก็มาใช้ IoC คือ Parsley แล้วก็ RobotLegs (ตอนนี้ใช้ตัวนี้ ยังไม่ได้ศึกษาตัวอื่นเพิ่ม)
ทั้ง MVC และ IoC เป็น framework ที่เข้ามาช่วยในการพัฒนางานให้เป็นระบบ เคยหาอ่านตามเว็บฝรั่งเพื่อหาเหตุผลว่าทำไมต้องใช้ และประกอบการพิจารณาว่า framework แต่ละอันจะใช้กับงานอะไร พอสรุปได้คร่าวๆ ว่า

ข้อดี
-ทุกอันมีดีของตัวเอง เพียงแต่ทางทีมได้ทำความเข้าใจการพัฒนาระบบที่ตรงกัน
-สมาชิกในทีมสามารถทำงานแทนกันได้
-สะดวกในการกลับมาปรับปรุงในภายหลัง
-แยกโค้ดกับดีไซน์ออกจากกัน
-สนับสนุนความเป็นอิสระต่อกันระหว่างระบบย่อยต่างๆ
-เหมาะกับระบบใหญ่ที่ต้องใช้ทีมงานหลายคน เช่น พวก enterprise software
-ส่วนใหญ่จะเทคะแนนไปทาง IoC ซึ่งไม่แน่ใจว่าเพราะมันถูกจริตกับผมหรือเปล่า เลยเชียร์ :D

ข้อเสีย
-มันเหมาะกับงานระบบ ซึ่ง flex เองอาจสู้ภาษาอื่นไม่ได้ตรงนี้ แต่ก็ใช่ว่าจะทำไม่ได้ ซึ่งก็เอาจุดเด่นเรื่องอื่น เช่น ความสวยงาม การแสดงผลเอฟเฟล็ค ฯลฯ มาแทน ซึ่งงานน่าจะไปในทางดีไซน์
-ต้องใช้เวลา ซึ่งบางทีเราทำคนเดียวหรือทีมงานขนาดเล็กอาจกระทบกับงานที่ทำอยู่
-การยึดตามรูปแบบของ framework บางทีอาจจำกัดอิสระในการสร้างสรรค์ความแปลกใหม่
———————-
การพิจารณาเลือก เขาบอกว่า no matter to choose ประมาณว่า ไม่มีสาระที่จะตัดสินว่าอะไรดีกว่ากัน เอามาพูดกันไม่จบหรอกว่าจะเลือกอะไร ขอให้เลือกเพื่อเป็นตัวบังคับกลายๆ ว่าพัฒนาไปในแนวทางเดียวกัน ดูเป็นระบบ ช่วยปรับปรุงแก้ไขง่าย ฯลฯ ตัวอย่างการถกเถียงก็อย่างเช่นว่า

บางตัวพัฒนาเวอร์ชั่นใหม่ๆ สนับสนุนการทำงานด้านต่างๆ หลากหลาย ซึ่งบางคนอาจกังวลว่าต้องใช้เวลาการเรียนรู้อยู่เรื่อยๆ อาจกระทบกับงานที่ทำอยู่ framework ไม่นิ่ง

บางตัวได้หยุดพัฒนาไปอาจด้อยในเรื่องความทันสมัย แต่ก็ดีในเรื่องความแน่นอนของเวอร์ชั่นที่มีการเปลี่ยนแปลงน้อย ทำให้เรียนรู้ครั้งเดียวอาจทำได้หลายงานมากกว่า ซึ่งทำให้มีเวลาไปศึกษาระบบงานที่จะเอามาสร้างซอฟต์แวร์ของเรา
———————
แต่ในใจโปรแกรมเมอร์เองก็คงอยากเลือกตัวที่มีความนิยม เพราะนั่นหมายถึงความสะดวกในการหาข้อมูลในระหว่างพัฒนาหรือปรับปรุงในภายหลัง ซึ่งผมเองก็เลือกไม่ใช่เพราะพิจารณาว่าอันไหนดี หาข้อมูลมาช่วงหนึ่งรู้สึกว่ามันไม่ได้บทสรุปว่าอันไหนเหมาะกับงานอะไร จึงเลือกเอาตัวที่มีผู้ใช้มาก ข้อมูลเยอะ เว็บหางานฟรีแลนซ์ของฝรั่งเขาพูดถึงอะไรบ่อย แบบนี้มากกว่าครับ ตอนนี้กำลังมอง Swiz อยู่รู้สึกว่าคนก็ใช้เยอะเป็นอันดับต้นๆ เหมือนกัน อันที่ใช้อยู่ก็ดีอยู่แล้วครับ ตอบโจทย์ได้เยอะในระดับหนึ่ง พยายามมองทางเลือกใหม่ๆ ตามความเหมาะสมครับ

Mini project : Send SMS with Flex App and Google Calendar

หัวข้อนี้เราจะมาลองทำ Mini project โดยประยุกต์ใช้ Flex ร่วมกับเทคโนโลยีอื่น ซึ่งในที่นี้เราจะใช้ feature ของ Google calendar ที่สามารถส่ง SMS แจ้งเตือนกิจกรรมที่เราบันทึกไว้ได้ มาใช้ประโยชน์ โดยใช้ Library ใน Zend Frameworks ช่วยในการส่งข้อมูลจาก Flex app ไปยัง Google calendar อีกที

(ซึ่งจริงๆ ผมได้รับอนิสงฆ์จากคนที่มาเรียนแล้วเอาความรู้และการใช้งานของเทคโนโลยีตัวนี้มาให้ผมช่วยดูเพื่อเอาไปประยุกต์ใช้ ต้องขอขอบคุณมากๆ ครับ การถ่ายทอดอะไรให้กับคนอื่นก็สามารถทำให้เราได้รับความรู้ได้เช่นกัน)

ขั้นตอนหลักๆ มีดังนี้

1. Google calendar setting

2. Create PHP client access file

3. Create Flex web app

————————————-

1. Google calendar setting

ก่อนอื่นเราต้องมี gmail account ก่อน จากนั้นให้เข้าไปที่ Your_Gmail_Account > Account Settings ดังภาพ

จากนั้นเลือก My product > Calendar – Settings ดังภาพ

จะมาที่หน้า Calendar Settings ให้เลือก Mobile Setting จากนั้นตั้งค่าต่างๆ ดังนี้

Country : Thailand

Phone number : ใส่เบอร์โทรโดยใส่ระหัสประเทศนำหน้า เช่น เบอร์โทร 0879999999 ให้ใส่เป็น +66879999999

จากนั้นกดปุ่ม Send Verification Code ทาง Google ก็จะส่ง SMS เป็นหมายเลขสำหรับ Verify เพื่อรับข้อมูลการแจ้งเตือนจาก Google calendar

————————————-

2. Create PHP client access file

การติดต่อกับ Google Calendar เราจะใช้ Library ใน Zend Frameworks โดยสามารถ ​Download ได้ที่ http://framework.zend.com/download/latest

เมื่อเรา Download มาแล้ว Unzip แล้วนำ Zend Frameworks ไปวางไว้ที่

LocalServerApp
|-frameworks
|    |-Zend
|    |-…
|-your_web_root_directory_such_as_www_or_htdocs_or_public_html_etc

ตัวอย่างในรูป

จากนั้นสร้างไฟล์ PHP client access file ดังนี้

<?php

// Set up debug
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", "on");

// Set up include path for Zend Framework, this path is assuming a frameworks
// folder contains the Zend package on the same level as your public_html or www folder.
//ini_set("include_path", ini_get("include_path") . ":../frameworks");
//ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR ."../frameworks");
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] . "/../frameworks");

require_once 'Zend/Loader.php';

Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_HttpClient');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

function createEvent($client, $title = 'Title', $desc='Description content', $where = 'Somewhere',
        $startDateTime = '2010-01-20T00:00+07:00', $endDateTime = '2010-01-20T00:00+07:00')
{
    $gdataCal = new Zend_Gdata_Calendar($client);
    $newEvent = $gdataCal->newEventEntry();

    $newEvent->title = $gdataCal->newTitle($title);
    $newEvent->where = array($gdataCal->newWhere($where));
    $newEvent->content = $gdataCal->newContent("$desc");

    $when = $gdataCal->newWhen();
    $when->startTime = "{$startDateTime}";
    $when->endTime = "{$endDateTime}";

    $reminder = $gdataCal->newReminder();
    $reminder->method = "sms";
    $reminder->minutes = "10";

    $when->reminders = array($reminder);

    $newEvent->when = array($when);

    // Upload the event to the calendar server
    // A copy of the event as it is recorded on the server is returned
    $createdEvent = $gdataCal->insertEvent($newEvent);

    return $createdEvent->id->text;
}

$user = $_REQUEST["user"];
$pass = $_REQUEST["pass"];
$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME; // predefined service name for calendar

$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);

$strStartDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 14, date("s"), date("m"), date("d"), date("Y")));
$strEndDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 15, date("s"), date("m"), date("d"), date("Y")));

createEvent($client, $_REQUEST["sms"],
                'Title',
                'Detail',
                $strStartDateTime, $strEndDateTime);

echo $_REQUEST['sms'];

————————————-

3. Create Flex web app

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:views="org.robotlegs.sawasdeesms.views.*">
	<s:layout>
		<s:VerticalLayout paddingLeft="4" paddingRight="4" paddingBottom="4" paddingTop="4"/>
	</s:layout>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<views:MainView width="{this.width}" height="{this.height}"/>

</s:Application>

MainView.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
		  xmlns:s="library://ns.adobe.com/flex/spark"
		  xmlns:mx="library://ns.adobe.com/flex/mx"
		  horizontalAlign="center"
		  preinitialize="mainViewInit()">

	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.core.FlexGlobals;
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.AsyncToken;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.http.HTTPService;

			private var arr:Array = [
				{fullname:'John Doe', email:'johndoe@gmail.com', pwd:'mypassword', selected:true}
			];

			[Bindable] private var ac:ArrayCollection;

			private var mainViewPopUp:MainPopUp;

			private var hs:HTTPService;
			private var urlVar:URLVariables;

			private function mainViewInit():void{
				ac = new ArrayCollection( arr );

				hs = new HTTPService();
				hs.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
				hs.url = '../php_service/sms.php';
				hs.addEventListener(ResultEvent.RESULT, httpServiceResult );

				urlVar = new URLVariables();

			}

			public function onSelect(obj:Object, selected:Boolean):void{
				obj.selected = selected;
				ac.setItemAt( obj, ac.getItemIndex( obj ) );

			}

			protected function btAdd_clickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			private function onFormSubmit( event:Event ):void{
				if( mainViewPopUp.useForm == MainPopUp.FORM_ADD ){
					ac.addItem(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:true
						}
					);
				}else{
					ac.setItemAt(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:dg.selectedItem.selected
						},
						dg.selectedIndex
					);
				}

			}

			protected function btRemove_clickHandler(event:MouseEvent):void
			{
				Alert.show('ต้องการลบรายชื่อ ใช่หรือไม่?', 'คำเตือน', 3, this, onConfirmRemove );
			}

			private function onConfirmRemove( event:CloseEvent ):void{
				if( event.detail == Alert.YES ){
					ac.removeItemAt( dg.selectedIndex );
				}
			}

			protected function dg_doubleClickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.useForm = MainPopUp.FORM_EDIT;
				mainViewPopUp.txtInName.text = dg.selectedItem.fullname;
				mainViewPopUp.txtInEmail.text = dg.selectedItem.email;
				mainViewPopUp.txtInPwd.text = dg.selectedItem.pwd;
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			protected function btSend_clickHandler(event:MouseEvent):void
			{
				urlVar.sms = txtAr.text;
				urlVar.user = ac.getItemAt( 0 ).email;
				urlVar.pass = ac.getItemAt( 0 ).pwd;
				var token:AsyncToken = hs.send(urlVar);
				token.index = 0;
			}

			protected function httpServiceResult( event:ResultEvent ):void{
				var token:AsyncToken;

				trace( event.token.index );
				trace( event.result );
				if( ( event.token.index + 1 ) < ac.length ){
					urlVar.user = ac.getItemAt( event.token.index + 1 ).email;
					urlVar.pass = ac.getItemAt( event.token.index + 1 ).pwd;
					token = hs.send(urlVar);
					token.index = event.token.index + 1;
				}else{
					Alert.show('ส่ง SMS แล้วค่ะ', 'ขอบคุณที่ใช้บริการ');
				}
			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<s:VGroup>
		<s:HGroup width="100%" height="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btAdd" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/add.png')"
					   click="btAdd_clickHandler(event)"/>
			<mx:Button height="28" id="btRemove" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/remove.png')"
					   enabled="{dg.selectedIndex + 1}"
					   click="btRemove_clickHandler(event)"/>
		</s:HGroup>

		<mx:DataGrid id="dg" dataProvider="{ac}"
					 doubleClickEnabled="true" doubleClick="dg_doubleClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn width="200" dataField="fullname" headerText="ชื่อ - นามสกุล"/>
				<mx:DataGridColumn width="200" dataField="email" headerText="อีเมล"/>
				<!--
				<mx:DataGridColumn dataField="selected" headerText="ส่งข้อความ ">
					<mx:itemRenderer>
						<fx:Component>
							<mx:Box horizontalAlign="center">
								<s:CheckBox id="cb" selected="{data.selected}"
											change="outerDocument.onSelect(data, cb.selected)"/>
							</mx:Box>
						</fx:Component>
					</mx:itemRenderer>
				</mx:DataGridColumn>
				-->
			</mx:columns>
		</mx:DataGrid>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<s:Label text="*หมายเหตุ Double-click เพื่อแก้ไข"/>
		</s:HGroup>

		<mx:Spacer height="50"/>

	</s:VGroup>

	<s:VGroup>
		<s:TextArea id="txtAr" width="400"/>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btSend" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/next.png')"
					   label="ส่ง SMS"
					   click="btSend_clickHandler(event)"/>
		</s:HGroup>
	</s:VGroup>

</s:VGroup>

MainPopUp.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" close="titlewindow1_closeHandler(event)">

	<fx:Metadata>
		[Event(name="formSubmit", type="mx.events.Event")]
	</fx:Metadata>

	<s:layout>
		<s:VerticalLayout/>
	</s:layout>

	<fx:Script>
		<![CDATA[
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;

			public static const FORM_ADD:String = 'formAdd';
			public static const FORM_EDIT:String = 'formEdit';

			public var useForm:String = FORM_ADD;

			protected function titlewindow1_closeHandler(event:CloseEvent):void
			{
				PopUpManager.removePopUp( this );
			}

			protected function btSubmit_clickHandler(event:MouseEvent):void
			{
				dispatchEvent( new Event( 'formSubmit' ) );
				PopUpManager.removePopUp( this );
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<s:controlBarContent>
		<mx:Spacer width="100%"/>
		<mx:Button height="28" id="btSubmit"
				    icon="@Embed(source='assets/aesthetica-version-2/png/24x24/accept.png')"
					click="btSubmit_clickHandler(event)"/>
	</s:controlBarContent>
	<mx:Form>
		<mx:FormItem label="ชื่อ">
			<s:TextInput id="txtInName" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="อีเมล">
			<s:TextInput id="txtInEmail" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="รหัสผ่าน">
			<s:TextInput displayAsPassword="true" id="txtInPwd" width="200"/>
		</mx:FormItem>
	</mx:Form>

</s:TitleWindow>

————————————-

งานนี้จะเอาไปใช้อะไรได้บ้าง ก็อย่างเช่น เราไม่สามารถอยู่หน้าคอมฯ ได้ตลอด สมมติเราทำระบบสำหรับสั่งซื้อสินค้าเมื่อมีออเดอร์เข้ามาก็ส่ง SMS ให้แจ้งเตือนให้เรารู้ เพื่อจะได้บริการลูกค้าได้เร็วขึ้น หรือจะทำเป็นระบบแจ้งเตือนการนัดประชุมในองค์กรขนาดเล็ก ช่วยประหยัดค่าใช้จ่ายค่าบริการ SMS หรือจะทำเป็นแจ้งเตือนการนัดหมายกับลูกค้า ฯลฯ แล้วแต่จะเอาไปประยุกต์ แต่ก็นะของฟรี บางทีก็ missing text บ้างไรบ้าง แต่ก็ไม่หนักหนาอะไรเกินไป ;)

Introduce to setTimeOut function and Timer class

แนะนำการจัดการเวลาโดยใช้ setTimeOut function และ Timer


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

<s:layout>
<s:VerticalLayout/>
</s:layout>

<fx:Script>

<![CDATA[

import flash.utils.setTimeout;
import mx.controls.Text;

private var timerOnce:Timer;
private var timerRepeat:Timer;

private function onBt1Click():void{

var i1:int = 1;
var i2:int = 2;
trace('BT1 :: i1=' + i1 + ', i2=' + i2 );

setTimeout(  function( i1:int, i2:int ):void{ trace('BT1 passed 1000 ms :: i1=' + i1 + ', i2=' + i2 ); }, 1000, i1, i2 );

i1 = 11;
i2 = 22;
trace('BT1 :: i1=' + i1 + ', i2=' + i2 );
}

private function onBt2Click():void{

var i1:int = 1;
var i2:int = 2;
trace('BT2 :: i1=' + i1 + ', i2=' + i2 );

setTimeout( onSetTimeOut, 1000, i1, i2 );

i1 = 11;
i2 = 22;
trace('BT2 :: i1=' + i1 + ', i2=' + i2 );
}

private function onSetTimeOut(i1:int, i2:int):void{
trace('BT2 passed 1000 ms :: i1=' + i1 + ', i2=' + i2 );
}

private function onTimerOnceClick():void{
if( timerOnce == null ){
timerOnce = new Timer( 1000, 1 ); // 1000 ms round
timerOnce.addEventListener(TimerEvent.TIMER, onTimerOnceRun);
timerOnce.start();
}
}

private function onTimerOnceRun(event:TimerEvent):void{
trace( 'timerOnce.currentCount=' + timerOnce.currentCount );
}

private function onTimerRepeatClick():void{
if( timerRepeat == null ){
timerRepeat = new Timer( 1000 ); // 1000 ms round
timerRepeat.addEventListener( TimerEvent.TIMER, onTimerRepeatRun);
timerRepeat.start();
}
}

private function onTimerRepeatRun(event:TimerEvent):void{
trace( 'timerRepeat.currentCount=' + timerRepeat.currentCount );
}

private function onTimerStartStop():void{
if( timerRepeat != null ){
if( timerRepeat.running ){
timerRepeat.stop();
}else{
timerRepeat.start();
}
}
}

]]>

</fx:Script>

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

<s:Button label="set time out 1" click="onBt1Click()"/>

<s:Button label="set time out 2" click="onBt2Click()"/>

<s:Button label="timer once" click="onTimerOnceClick()"/>

<s:HGroup>

<s:Button label="timer repeat" click="onTimerRepeatClick()"/>

<s:Button label="Start or Stop" click="onTimerStartStop()"/>

</s:HGroup>

</s:Application>

Using AsyncToken class by RemoteObject or HTTPService

การใช้ RemoteObject หรือ HTTPService ติดต่อไปยัง Server เพื่อเอาข้อมูลมาใช้งาน ในบางครั้งเราอาจมีความจำเป็นบางอย่างที่ต้องการจำแนกแยกแยะว่าข้อมูลที่ได้มาด้วยเมธอด(method)หรือโอเปอเรชั่น(operation)นั้น เกิดขึ้นจากกรณีใด ในการแก้ปัญหานี้เราสามารถกำหนด Token ให้กับ RemoteObject เพื่อเป็นตัวช่วยในการตัดสินใจได้

(เคยเข้าใจผิดกับคำว่า Token กับ Totem ในหนังเรื่อง Inception ดีนะที่มีคนให้ความชัดเจนเรื่องนี้ไม่งั้นคงทำให้คนที่มาเรียนเข้าใจอะไรผิดๆ ไป)

Example how to use AsyncToken by RemoteObject


<?xml version="1.0" encoding="utf-8"?>

<!-- TokenExampleApp.mxml -->

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

xmlns:s="library://ns.adobe.com/flex/spark"

xmlns:mx="library://ns.adobe.com/flex/mx"

applicationComplete="initApp()">

<s:layout>

<s:VerticalLayout/>

</s:layout>

<fx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import mx.messaging.messages.RemotingMessage;

import mx.rpc.AsyncToken;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

[Bindable] private var ac:ArrayCollection;

private function initApp():void{

ac = new ArrayCollection(

[

{datetime:"-"},

{datetime:"-"},

{datetime:"-"},

{datetime:"-"},

{datetime:"-"}

]

);

}

protected function bt_clickHandler(event:MouseEvent):void

{

var token:AsyncToken = ro.getOperation('getDateTime').send( dg.selectedItem );

token.selectedIndex = dg.selectedIndex; // Set token property and value here

}

protected function ro_faultHandler(event:FaultEvent):void

{

}

protected function ro_resultHandler(event:ResultEvent):void

{

var rMsg:RemotingMessage = event.token.message as RemotingMessage;

switch( rMsg.operation ){

case 'getDateTime':

ac.setItemAt( event.result, event.token.selectedIndex ); //Check token property value here

break;

}

}

]]>

</fx:Script>

<fx:Declarations>

<!-- Place non-visual elements (e.g., services, value objects) here -->

<s:RemoteObject id="ro" destination="zendamf" source="TokenExService"

fault="ro_faultHandler(event)"

result="ro_resultHandler(event)"/>

</fx:Declarations>

<s:Button id="bt" label="Send" enabled="{dg.selectedIndex + 1}" click="bt_clickHandler(event)"/>

<mx:DataGrid id="dg" dataProvider="{ac}">

<mx:columns>

<mx:DataGridColumn dataField="datetime" width="200"/>

</mx:columns>

</mx:DataGrid>

</s:Application>


<?php

/**
 * Description of TokenExService
 *
 * @author noppadonsodram
 */

class TokenExService {
    //put your code here

    function getDateTime($vo){
        $vo->datetime = date("F j, Y, g:i:s a");
        return $vo;
    }

}

หัวข้อเกี่ยวกับการใช้ ​RemoteObject หรือ HTTPService

http://thaiflexdev.wordpress.com/2009/10/04/introduction-to-zendamf/

http://thaiflexdev.wordpress.com/2009/07/11/amfphp-beginnign-level/

http://thaiflexdev.wordpress.com/2010/07/15/php-variable-value-when-send-by-httpservice-or-remoteobject-2/

http://thaiflexdev.wordpress.com/2010/07/15/send-array-to-flex-app/

Repeaters require a halo container based parent

ปัญหาการใช้งาน Repeater  ใน Flex 4 ที่แจ้งข้อผิดพลาดว่า Repeaters require a halo container based parent หมายความว่า Repeater จะต้อง Halo container เป็น Parent based หรือมี Parent tag ที่มี namespace ขึ้นต้นด้วย <mx:/> เท่านั้น

ดังนั้นเราไม่สามารถใช้แบบนี้ได้

<s:VGroup>
<mx:Repeater id="repeatBt" dataProvider="{ac}">
<s:Button width="100%" height="40"/>
</mx:Repeater>
</s:VGroup>

ต้องจัด Repeater ให้อยู่ใน <mx:/> ดังนี้

<mx:VBox id="vBx" width="100%">
<mx:Repeater id="repeatBt" dataProvider="{ac}">
<s:Button width="100%" height="40"/>
</mx:Repeater>
</mx:VBox>

Firebug & AMF Explorer

ปกติเวลาดีบัก เราสามารถใช้ Flex/Flash Builder ร่วมกับกับ Flash Player Debugger และยังมืทางเลือกอื่นสำหรับใช้เป็น Monitor เพื่อดูค่าการรับส่งระหว่าง Flex Web App กับ Server ได้ โดยใช้เครื่องมื่อต่อไปนี้

http://getfirebug.com/

http://amfexplorer.riaforge.org/

https://addons.mozilla.org/en-US/firefox/addon/amf-explorer/

ZamfBrowser is Zend Amf Browser

http://stackoverflow.com/questions/2628416/service-browser-for-zend-amf

เอาไว้ดู คล้ายๆ กับใน amfphp/browser/

วิธีการติดตั้งและทดสอบ

1. สร้างโฟลเดอร์ frameworks ไว้ที่เดียวกับ Web root directory เช่น Applications/XAMPP/xamppfiles/frameworks ใน Mac OS X ที่ติดตั้ง Xampp หรือ C:\AppServ\frameworks ใน Windows ที่ติดตั้ง AppServ

2. ดาวน์โหลด Zend Frameworks จาก http://framework.zend.com/  จากนั้น Unzip  แล้ว Copy โฟลเดอร์ชื่อ Zend ซึ่งอยู่ที่ตำแหน่ง  ZendFrameworks-#.#.#/library/Zend ไปไว้ในโฟลเดอร์ frameworks ที่สร้างไว้ตามข้อ 1. ดังนี้ Applications/XAMPP/xamppfiles/frameworks/Zend หรือ C:\AppServ\frameworks\Zend

3. ดาวน์โหลดโปรแกรม ZamfBrowser จาก http://zamfbrowser.riaforge.org/index.cfm แล้ว Unzip จะได้โฟลเดอร์ ZamfBrowser_v.#.# เปลี่ยนชื่อเป็น ZamfBrowser และนำไปไว้ที่ Applications/XAMPP/xamppfiles/htdocs/ZamfBrowser หรือ C:\AppServ\www\ZamfBrowser จากนั้นติดตั้งโปรแกรมโดยใช้ไฟล์ ZamfBrowser/ZamfBrowser.air แล้วเปิดโปรแกรม

4. ตั้งค่า Services gateway คลิกที่ [Settings] จากนั้นคลิ๊ก [+]

ใส่ข้อมูลสำหรับ Add ZendAMF Server

ZendAMF Server Name : ZamfBrowser

Services Gateway : http://127.0.0.1/ZamfBrowser/zendamf_gateway_example.php

คลิก [Add Server]

ที่ Combo Box เลือก ZendAmfServiceBrowser และ getServices ตามลำดับ แล้วคลิก [Invoke Method] เพื่อทดสอบ

5. สร้างตารางในฐานข้อมูลเพื่อทดสอบ

CREATE DATABASE zamfbrowser;
USE zamfbrowser;
DROP TABLE IF EXISTS `zamfbrowser_z`;
CREATE TABLE `zamfbrowser_z` (
`z_auto_id` int(11) NOT NULL AUTO_INCREMENT,
`z_data` varchar(255) NOT NULL,
`z_label` varchar(255) NOT NULL,
`z_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`z_auto_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

6. สร้างไฟล์ Service file ไว้ที่ [Web root directory]/ZamfBrowser/zendamf/

DbMySQLConst.php

<?php
/* PHP GENERATOR VERSION : MySQL AMFPHP 1.2 Direct connect to DB */
/** 		 * DbMySQLConst class */
class DbMySQLConst {
//put your code here
const HOST = "localhost";
const USERNAME = "root";
const PASSWORD = "";
const DB_NAME = "zamfbrowser";
}

ZamfbrowserVO.php

<?php
/*  * To change this template, choose Tools | Templates * and open the template in the editor. */
/** * Description of ZamfbrowserVO * * @author noppadonsodram */
class ZamfbrowserVO {
//put your code here
var $z_auto_id;
var $z_data;
var $z_label;
var $z_timestamp;
}

ZamfbrowserService.php

<?php
/* * To change this template, choose Tools | Templates * and open the template in the editor. */
/** * Description of ZamfbrowserService * * @author noppadonsodram */require_once 'Zend/Db/Adapter/Pdo/Mysql.php';require_once 'DbMySQLConst.php';require_once 'ZamfbrowserVO.php';
class ZamfbrowserService {
 var $db;
var $table = "zamfbrowser_z";
var $pk = "z_auto_id";
 //put your code here
function __construct() {
$this->db = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => DbMySQLConst::HOST,
'username' => DbMySQLConst::USERNAME,
'password' => DbMySQLConst::PASSWORD,
'dbname' => DbMySQLConst::DB_NAME
));
}
 function __destruct() {
$this->db->closeConnection();
}
 function insertRow($vo) {
$vo = get_object_vars($vo);
return $this->db->insert($this->table, $vo);
}
 function updateRow($vo) {
$vo = get_object_vars($vo);
return $this->db->update($this->table, $vo, $this->pk."='". $vo[$this->pk]."'");
}
 function deleteRow($vo) {
$vo = get_object_vars($vo);
return $this->db->delete($this->table, $this->pk."='". $vo[$this->pk]."'");
}
 function selectRow() {
$rows = $this->db->select()
->from($this->table)
->order($this->pk)
->query()->fetchAll();
$ary = array();
foreach ($rows as $row) {
$vo = new ZamfbrowserVO();
$vo->z_auto_id = $row['z_auto_id'];
$vo->z_data = $row['z_data'];
$vo->z_label = $row['z_label'];
$vo->z_timestamp = $row['z_timestamp'];
$ary[] = $vo;
}
 return $ary;
}
}

7. ระบุ Service file ไว้ใน Gateway file

zendamf_gateway_example.php

<?php
// Set up debug
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", "on");

// Set up include path for Zend Framework, this path is assuming a frameworks
// folder contains the Zend package on the same level as your public_html or www folder.
//ini_set("include_path", ini_get("include_path") . ":../frameworks");
//ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR ."../frameworks");
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] ."/../frameworks");

// Require Zend_Amf_Server
require_once( "Zend/Amf/Server.php" );

// *ZAMFBROWSER IMPLEMENTATION*
// Require the ZendAmfServiceBrowser class, required to retrieve the list of methods on the ZendAMF server.
require_once( "browser/ZendAmfServiceBrowser.php" );
require_once( "zendamf/ZamfbrowserService.php" );

// Start Server
$server = new Zend_Amf_Server();

// Register ZendAMF Service classes
//$server->setClass( "YourServiceClass" );
//$server->setClass( "AnotherServiceClass" );
// *ZAMFBROWSER IMPLEMENTATION*
// Add the ZendAmfServiceBrowser class to the list of available classes.
$server->setClass("ZendAmfServiceBrowser");
$server->setClass("ZamfbrowserService");

// *ZAMFBROWSER IMPLEMENTATION*
// Set this reference the class requires to the server object.
ZendAmfServiceBrowser::$ZEND_AMF_SERVER = $server;

// Handle the AMF request
echo($server->handle());

8. ทดสอบ Service file ด้วย ZamfBrowser

Class : ZamfbrowserService

Method : insertRow

Class : ZamfbrowserService

Method : selectRow

9. สร้าง Flex Project

Project : ZamfApp

คลิ๊กขวา ZamfApp->Properties->Flex Compiler

Edit ค่า Additional compiler arguments จาก -locale en_US เป็น locale en_US -services “services-config.xml”

คลิ๊ก [Apply]->[OK]

ZamfApp.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">

<s:layout>
<s:VerticalLayout/>
</s:layout>

<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.messaging.messages.RemotingMessage;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.utils.ArrayUtil;
import zendamf.ZamfbrowserVO;

[Bindable] public var ac:ArrayCollection;

protected function faultHandler(event:FaultEvent):void
{
Alert.show('fault');
}

protected function rmtObjZamfbrowser_resultHandler(event:ResultEvent):void
{
var rMsg:RemotingMessage = event.token.message as RemotingMessage;
switch( rMsg.operation ){
case 'insertRow':
break;
case 'selectRow':
ac = new ArrayCollection( ArrayUtil.toArray( event.result ) );
break;

case 'deleteRow':
break;
}
}

protected function btSubmit_clickHandler(event:MouseEvent):void
{
var zamfValObj:ZamfbrowserVO = new ZamfbrowserVO();

zamfValObj.z_auto_id = txtInID.text;
zamfValObj.z_data = txtInData.text;
zamfValObj.z_label = txtInLabel.text;
zamfValObj.z_timestamp = txtInTimestamp.text;

rmtObjZamfbrowserService.getOperation('insertRow').send(zamfValObj);
}

protected function btCall_clickHandler(event:MouseEvent):void
{
rmtObjZamfbrowserService.getOperation('selectRow').send();
}

]]>

</fx:Script>

<fx:Declarations>
<s:RemoteObject id="rmtObjZamfbrowserService"
showBusyCursor="true"
source="ZamfbrowserService"
destination="zend"
fault="faultHandler(event)"
result="rmtObjZamfbrowser_resultHandler(event)"/>

</fx:Declarations>

<mx:Form>
<mx:FormItem label="ID">
<s:TextInput id="txtInID"/>
</mx:FormItem>

<mx:FormItem label="Data">
<s:TextInput id="txtInData"/>
</mx:FormItem>

<mx:FormItem label="Label">
<s:TextInput id="txtInLabel"/>
</mx:FormItem>
<mx:FormItem label="Time">
<s:TextInput id="txtInTimestamp"/>
</mx:FormItem>

<s:Button id="btSubmit"
click="btSubmit_clickHandler(event)" label="Submit"/>
</mx:Form>

<s:Button id="btCall"
click="btCall_clickHandler(event)" label="Call"/>

<mx:AdvancedDataGrid id="avd" dataProvider="{ac}"/>

</s:Application>

ZamfbrowserVO.as

package zendamf
{
[RemoteClass(alias="zendamf.ZamfbrowserVO")]
[Bindable]
public class ZamfbrowserVO
{
public var z_auto_id:String;
public var z_data:String;
public var z_label:String;
public var z_timestamp:String;
}
}

services-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="zend">
<channels>
<channel ref="my-zend"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-zend">
<endpoint uri="../zendamf_gateway_example.php"/>
</channel-definition>
</channels>
</services-config>

10. Run Flex with Zend Amf

Birdeye : Information Visualization and Visual Analytics Library

Wow!

http://code.google.com/p/birdeye/

QR Code component

http://ntt.cc/2010/05/01/read-qr-code-with-qrcodereader-open-source-as3-library.html

http://workflowflash.com/5865/qr-code-reader-as3.php

http://jbpin.wordpress.com/2010/08/07/as3-qr-code-encoder-library/

http://jbpin.herobo.com/index.html#app=4da5&92cb-selectedIndex=0

Follow

Get every new post delivered to your Inbox.