Call JS function from Flash

No Comments

งานอย่างนึงที่ต้องทำผ่านมาคือเรียกโปรแกรมที่เขียนด้วย Adobe AIR จากเว็บ แต่เรียกอย่างเดียวมันธรรมดาไป ต้องมีการรับค่าจากฟอร์มในเว็บด้วย! ตอนแรกก็คิดว่าไม่ยาก ที่ไหนได้เจออิทธิฤทธ์ของ IE6 เข้าไปถึงกับงงเลยทีเดียว แต่จะเขียนแล้วก็เขียนถึงวิธีเรียกทั้งหมดเลยละกัน

ใน Flex framework มีคลาสสำหรับติดต่อกับ Javascript จะใช้เรียกฟังก์ชั่น หรือจะให้ฟังก์ชั่น Javascript มาเรียกใน Flash คือ flash.external.ExternalInterface เวลาเรียกฟังก์ชั่น Javascript จาก Flash ก็ง่ายๆ ด้วยคำสั่ง

ExternalInterface.call("javascriptFunctionName", "argument1", "argument2", ... , "argumentN")

แต่ความยากไม่ได้อยู่ที่ตรงนี้ สิ่งที่ทำพลาดจนทำให้ต้องจดคือสิ่งที่อยู่ใน HTML เพราะบราวเซอร์แต่ละตัวเห็น tag object embed ต่างกัน สุดท้ายแล้วทางที่ดีคือคัดลอกเอาจากใน Flex API Document มานั่นแหละ ปลอดภัยที่สุด

     <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
             id="ExternalInterfaceExample" width="40" height="22"
             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
         <param name="movie" value="movie.swf" />
         <param name="quality" value="high" />
         <param name="bgcolor" value="#869ca7" />
         <param name="allowScriptAccess" value="sameDomain" />
         <embed src="movie.swf" quality="high" bgcolor="#869ca7"
             width="40" height="22" name="ExternalInterfaceExample" align="middle"
             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
             type="application/x-shockwave-flash"
             pluginspage="http://www.macromedia.com/go/getflashplayer">
         </embed>
     </object>

ลองมาดูของจริงเลยละกัน Javascript ในหน้านี้หน้าตาเป็นอย่างด้านล่าง

<script type="text/javascript">
function hello() {
     return 'Hello, World'
}
</script>
เมื่อกดปุ่มด้านล่าง จะเรียก function hello เพื่อเอามาแสดงใน Alert



ไม่ยากเท่าไหร่แต่เรื่องนี้สอนให้รู้ว่า จงทำตามเอกสารมันทุกอย่าง อย่าไปลองอะไรจากเว็บอื่นให้ปวดหัวอีก

Function declaration style and variable scope problem

3 Comments

เป็นปัญหาที่ยังไม่สามารถทำให้เกิดขึ้นอีกในโค้ดแบบอื่นได้ แต่เอามาเขียนไว้ก่อนเพราะมันสร้างความปวดหัวให้กับคนเขียน Javascript มาก่อนมากมาย และไม่รู้จะเจอมันอีกทีเหมื่อไหร่ แต่ไม่สามารถเอาโค้ดต้นฉบับทั้งก้อนมาใส่ในนี้ได้ เลยขอแค่เล่าแล้วเอา stacktrace มาแปะใส่ไว้ละกัน

เหตุการณ์เริ่มต้นเกิดจาก @pitiphong_p เจอบั๊กหนึ่งที่ไม่รู็จะแก้อย่างไรเข้าเพราะใน Flash Builder Debugger บอกว่าตัวแปรต่างๆ มีค่าครบสมบุรณ์หมด แต่ error บอกไม่สามารถหาตัวแปรนั้นได้

[Fault] exception, information=TypeError: Error #1010: A term is undefined and has no properties.
Fault, CheckboxListRenderer.as:21
 21            data.selected = checkbox.selected
(fdb) bt
#0   this = [Object 32588233, class='global'].<anonymous>(event=[Object 659079665, class='flash.events::Event']) at CheckboxListRenderer.as:21
#1   EventDispatcher/dispatchEventFunction() at <null>:0
#2   this = [Object 655130785, class='mx.controls::CheckBox'].EventDispatcher/dispatchEvent(_arg1=[Object 659079665, class='flash.events::Event']) at <null>:0
#3   this = [Object 655130785, class='mx.controls::CheckBox'].UIComponent/dispatchEvent(event=[Object 659079665, class='flash.events::Event']) at UIComponent.as:9440
#4   this = [Object 655130785, class='mx.controls::CheckBox'].Button/http://www.adobe.com/2006/flex/mx/internal::setSelected(value=true, isProgrammatic=false) at Button.as:1204
#5   this = [Object 655130785, class='mx.controls::CheckBox'].Button/clickHandler(event=[Object 591642521, class='flash.events::MouseEvent']) at Button.as:2798

ตอนแรกก็พยายามคาดเดาปัญหาไปต่าง ๆ อาจเกิดจากการ bind ตัวแปรผิดที่ หรือตอนกำหนดค่าผิด แต่ถ้าอย่างนั้น ทำไมตอน debug ถึงสามารถเอาค่าออกมาดูได้หละ ลองดูโค้ดเจ้าปัญหาซักนิดก่อนละกัน

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package sample
{
  import mx.binding.utils.BindingUtils
  import mx.containers.HBox
  import mx.controls.Alert
  import mx.controls.CheckBox
  import mx.core.IFactory
 
  import flash.events.Event
 
  public class CheckboxListRenderer extends HBox {
 
    [Bindable]
    public var listRenderer:IFactory
 
    public var checkbox:CheckBox = new CheckBox()
 
    private var instance:*
 
    private var checkboxChange = function(event:Event):void {
      if (data.hasOwnProperty('selected')) {
        data.selected = checkbox.selected
      }
    }
 
    public override function set data(item:Object):void {
      super.data = item
      if (item && item.hasOwnProperty('selected')) {
        checkbox.selected = item.selected
      }
    }
 
    protected override function createChildren():void {
      super.createChildren()
      addChild(checkbox)
 
      if (listRenderer != null) {
        instance = listRenderer.newInstance()
        addChild(instance)
      }
      trace("Checkbox list renderer created children")
    }
 
    protected override function commitProperties():void {
      super.commitProperties()
      horizontalScrollPolicy = "off"
      verticalScrollPolicy = "off"
 
      // Handle change in targeting phase for changing data selected flag.
      checkbox.addEventListener(Event.CHANGE, checkboxChange)
 
      BindingUtils.bindProperty(instance, "data", this, "data")
    }
 
  }
}

รับรองได้เลยว่าถ้าใครเอาโค้ดด้านบนไปลองเล่นดูเองจะไม่เจอปัญหาแน่นอน ซึ่งถึงวันนี้ยังไม่เข้าใจเหมือนกันว่าทำยังไงถึงจะเกิดปัญหาได้ แต่วิธีแก้นั้นง่ายมากคือ เปลี่ยนวิธีประกาศฟังก์ชั่นจาก

20
21
22
23
24
    private var checkboxChange = function(event:Event):void {
      if (data.hasOwnProperty('selected')) {
        data.selected = checkbox.selected
      }
    }

เป็น

20
21
22
23
24
    private function checkboxChange(event:Event):void {
      if (data.hasOwnProperty('selected')) {
        data.selected = checkbox.selected
      }
    }

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

Vietnam Trip

No Comments

Older Entries