| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <!DOCTYPE html |
| PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| <html lang="en-us" xml:lang="en-us"> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> |
| <meta name="DC.Type" content="topic"/> |
| <meta name="DC.Title" content="Deep linking"/> |
| <meta name="DC.Format" content="XHTML"/> |
| <meta name="DC.Identifier" content="WS2db454920e96a9e51e63e3d11c0bf69084-7bb5_verapache"/> |
| <link rel="stylesheet" type="text/css" href="commonltr.css"/> |
| <title>Deep linking</title> |
| </head> |
| <body id="WS2db454920e96a9e51e63e3d11c0bf69084-7bb5_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7bb5_verapache"><!-- --></a> |
| |
| |
| <h1 class="topictitle1">Deep linking</h1> |
| |
| <div> |
| <p/> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf69084-7cb6_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7cb6_verapache"><!-- --></a> |
| <h2 class="topictitle2">About deep linking</h2> |
| |
| |
| <div> |
| <p>One of the benefits of an application built with Flex is |
| that it can smoothly transition from state to state without having |
| to fetch a new page from the server and refresh the browser. By |
| avoiding the constant refreshing of pages, the end-user's experience |
| is more fluid and continuous. In addition, the load on the server is |
| greatly reduced because it need only return the application once, |
| rather than a new page every time the user changes views. </p> |
| |
| <p>However one of the advantages of a browser's page-oriented model |
| is that an application's navigational state is usually clearly coupled |
| to a URL. Thus, when the state of an application changes, the user |
| can usually do the following: </p> |
| |
| <ul> |
| <li> |
| <p>Bookmark the URL to get back to that state in the application </p> |
| |
| </li> |
| |
| <li> |
| <p>Email the URL to a friend</p> |
| |
| </li> |
| |
| <li> |
| <p>Use the Back and Forward buttons to navigate to recently |
| visited application states</p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>Losing the ability to couple a browser-managed URL to a specific |
| state of an application can be a fairly big drawback when creating |
| applications. Deep linking adds URL-mapping or “bookmarking” capabilities |
| to applications. At the high level, deep linking lets you do the |
| following:</p> |
| |
| <ul> |
| <li> |
| <p>Update the URL when the application state changes. This |
| also generates a new entry in the browser’s history.</p> |
| |
| </li> |
| |
| <li> |
| <p>Read in values from the URL to change the state of the application. |
| For example, a bookmark could load the application at a particular |
| state.</p> |
| |
| </li> |
| |
| <li> |
| <p>Recognize when the URL in the browser’s address bar has changed. |
| If the user clicks the Forward or Back button, or changes the URL |
| in the address bar, the application is notified and can react to |
| the change.</p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>What states of your application are bookmarkable will vary by |
| application, but possibilities include a login page, a product page, |
| a search result page, or a drill down view into data. </p> |
| |
| <p>Deep linking only works with certain browsers. The following |
| browsers support deep linking:</p> |
| |
| <ul> |
| <li> |
| <p>Microsoft Internet Explorer version 6 or later</p> |
| |
| </li> |
| |
| <li> |
| <p>FireFox for Windows and Macintosh</p> |
| |
| </li> |
| |
| <li> |
| <p>Safari for Macintosh</p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>In addition to requiring these browsers, deep linking requires |
| that the client browser also has JavaScript enabled. Deep linking |
| does not work with the standalone Adobe<sup>®</sup> Flash<sup>®</sup> Player or Adobe AIR™.</p> |
| |
| </div> |
| |
| <div class="nested2" id="WS2db454920e96a9e51e63e3d11c0bf64e50-7ffc_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf64e50-7ffc_verapache"><!-- --></a> |
| <h3 class="topictitle3">How deep linking works</h3> |
| |
| |
| <div> |
| <p>Deep linking relies on communication between the browser |
| and the application. The communication is bidirectional: if a change |
| occurs in the application, the browser must be notified, and if |
| a change in the browser occurs, then the application must be notified. |
| This communication is handled by the <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/BrowserManager.html" target="_blank">BrowserManager </a>class. |
| This class uses methods in the HTML wrapper’s JavaScript to handle |
| events, update the browser’s address bar, and call other methods. |
| Another class, <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/utils/URLUtil.html" target="_blank">URLUtil</a>, is provided to make |
| it easier to parse the URL as you read it in your application and |
| write it back to the browser.</p> |
| |
| <p>To use deep linking, you write ActionScript that sets and gets |
| portions of the URL to determine which state the application is |
| in. These portions are called fragments, and occur after the pound |
| sign (“#”) in the URL. In the following example, <samp class="codeph">view=1</samp> is |
| the fragment:</p> |
| |
| <pre class="codeblock"> http://my.domain.com#view=1</pre> |
| |
| <p>If the user navigates to a new view in your application, you |
| update the URL and set <samp class="codeph">view=2</samp> in the URL fragment. |
| If the user then clicks the browser’s Back button, the BrowserManager |
| is notified of the event, and gives you a chance to change the application’s |
| state in respond to the URL fragment changing back to <samp class="codeph">view=1</samp>.</p> |
| |
| <p>You typically use the methods of the URLUtil class to parse the |
| URL. This class provides methods for detecting the server name, |
| port number, and protocol of a URL. In addition, you can use the <samp class="codeph">objectToString()</samp> method |
| to convert an ActionScript object to a String that you then append |
| to the end of a URL. Alternatively, you can convert any number of |
| name/value pairs on a query string into an object by using the URLUtil |
| class’s <samp class="codeph">stringToObject()</samp> method. This lets you |
| then manipulate the fragment more easily in your application logic.</p> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested2" id="WS2db454920e96a9e51e63e3d11c0bf69084-7cb5_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7cb5_verapache"><!-- --></a> |
| <h3 class="topictitle3">Deploying applications that use |
| deep linking</h3> |
| |
| |
| <div> |
| <p>To use deep linking, your HTML wrapper requires that the |
| following files be deployed with your application:</p> |
| |
| <ul> |
| <li> |
| <p>history.css</p> |
| |
| </li> |
| |
| <li> |
| <p>history.js</p> |
| |
| </li> |
| |
| <li> |
| <p>historyFrame.html</p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>You must include the history.js and history.css files in your |
| wrapper. The following example imports the JS and CSS files:</p> |
| |
| <pre class="codeblock"> <!-- BEGIN Browser History required section --> |
| <link rel="stylesheet" type="text/css" href="history/history.css"/> |
| <script src="history/history.js" language="javascript"></script> |
| <!-- END Browser History required section --></pre> |
| |
| <p>You must also deploy the historyFrame.html file with your application. |
| This file must be located in the /history sub-directory; its location |
| is relative to your deployed application SWF file’s location. </p> |
| |
| <p>For the SDK, the files are located in the <em>sdk_install</em>/templates/swfobject/history directory.</p> |
| |
| </div> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf64e50-7ff4_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf64e50-7ff4_verapache"><!-- --></a> |
| <h2 class="topictitle2">Using the BrowserManager</h2> |
| |
| |
| <div> |
| <p>The BrowserManager is a Singleton class that acts as a |
| proxy between the browser and the application. It provides access |
| to the URL in the browser address bar similar to accessing the <samp class="codeph">document.location</samp> property |
| in JavaScript. When the URL changes in the browser, the BrowserManager |
| is notified of the event. You can then change the URL, respond to |
| the event, or block the event.</p> |
| |
| <p>To get a reference to the BrowserManager, you call its <samp class="codeph">getInstance()</samp> method. |
| This method returns the current instance of the manager, which implements |
| IBrowserManager. You can then call methods on the manager such as <samp class="codeph">setTitle()</samp> and <samp class="codeph">setFragment()</samp>. </p> |
| |
| <p>You can also listen to events on that manager. The events are <samp class="codeph">browserURLChange</samp>, <samp class="codeph">urlChange</samp>, |
| and <samp class="codeph">applicationURLChange</samp>. These events are described |
| in <a href="flx_deep_linking_dl.html#WS2db454920e96a9e51e63e3d11c0bf69084-7cbd_verapache">About |
| BrowserManager events</a>.</p> |
| |
| <p>You can also access properties of the manager. These properties |
| store the browser’s current title, plus the full URL and its sub-parts, <samp class="codeph">fragment</samp> and <samp class="codeph">base</samp>. The |
| properties are read-only, but you can use methods of the manager, |
| such as <samp class="codeph">setTitle()</samp> and <samp class="codeph">setFragment()</samp>, |
| to set the values of some of them.</p> |
| |
| <p>You typically call the <samp class="codeph">getInstance()</samp> method |
| when your application initializes. This returns an instance of the |
| Singleton BrowserManager. You then register an event listener for |
| the <samp class="codeph">browserURLChange</samp> event. This event is dispatched |
| when the user clicks the Back or Forward button in the browser. Finally, |
| you call the <samp class="codeph">init()</samp> method to initialize the BrowserManager. |
| The first parameter of the <samp class="codeph">init()</samp> method defines |
| the default fragment, and the second parameter defines the title |
| for the current page.</p> |
| |
| <p>The following example instantiates the BrowserManager, registers |
| the <samp class="codeph">parseURL()</samp> method to listen for <samp class="codeph">browserURLChange</samp> events, |
| and calls the <samp class="codeph">init()</samp> method with a blank fragment |
| as the default fragment. It sets the value of the page’s title to |
| “Test Deep Linking”:</p> |
| |
| <pre class="codeblock"> private function initApp():void { |
| browserManager = BrowserManager.getInstance(); |
| browserManager.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE, parseURL); |
| browserManager.init("", "Test Deep Linking"); |
| }</pre> |
| |
| <p>Calling the BrowserManager class’s <samp class="codeph">init()</samp> method |
| also gets the current URL as a property in the BrowserManager.</p> |
| |
| <p>If you set a value for the default fragment in the <samp class="codeph">init()</samp> method, |
| when the URL changes, the URL with the default fragment will be |
| entered in the browser’s history. This way, if the user clicks the |
| Back button or in some other way accesses this page from the browser’s |
| history, this fragment will be used. Also, the BrowserManager returns |
| the default fragment if there is nothing after the pound sign (“#”) |
| in the URL.</p> |
| |
| </div> |
| |
| <div class="nested2" id="WS2db454920e96a9e51e63e3d11c0bf69084-7cbc_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7cbc_verapache"><!-- --></a> |
| <h3 class="topictitle3">Updating the URL </h3> |
| |
| |
| <div> |
| <p>You use the BrowserManager’s <samp class="codeph">setFragment()</samp> method |
| to update the URL in the browser’s address bar. You can only change |
| the fragments in the URL. You cannot change the base of the URL, |
| including the server, protocol, or port numbers.</p> |
| |
| <p>When you use the <samp class="codeph">setFragment()</samp> method to change |
| the URL, you trigger an <samp class="codeph">applicationURLChange</samp> event.</p> |
| |
| <p>The following example updates the URL in the browser whenever |
| you change the active panel in the TabNavigator container. It also |
| keeps a record of the current URL and previous URL each time an <samp class="codeph">applicationURLChange</samp> event is |
| triggered.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/UpdateURLExample.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" |
| creationComplete="init();"> |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.events.BrowserChangeEvent; |
| import mx.managers.IBrowserManager; |
| import mx.managers.BrowserManager; |
| import mx.utils.URLUtil; |
| |
| private var browserManager:IBrowserManager; |
| |
| private function init():void { |
| browserManager = BrowserManager.getInstance(); |
| browserManager.addEventListener(BrowserChangeEvent.APPLICATION_URL_CHANGE, |
| logURLChange); |
| browserManager.init("", "Welcome!"); |
| } |
| |
| public function updateTitle(e:Event):void { |
| browserManager.setTitle("Welcome " + ti1.text + " from " + ti2.text + "!"); |
| } |
| |
| private function updateURL(event:Event):void { |
| var s:String = "panel=" + event.currentTarget.selectedIndex; |
| browserManager.setFragment(s); |
| } |
| |
| private function logURLChange(event:BrowserChangeEvent):void { |
| ta1.text += "APPLICATION_URL_CHANGE event:\n"; |
| ta1.text += " url: " + event.url + "\n"; // Current URL in the browser. |
| ta1.text += " prev: " + event.lastURL + "\n"; // Previous URL. |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:TabNavigator id="tn" width="300" change="updateURL(event)"> |
| <mx:Panel label="Personal Data"> |
| <mx:Form> |
| <mx:FormItem label="Name:"> |
| <mx:TextInput id="ti1"/> |
| </mx:FormItem> |
| <mx:FormItem label="Hometown:"> |
| <mx:TextInput id="ti2"/> |
| </mx:FormItem> |
| <mx:Button id="b1" click="updateTitle(event)" label="Submit"/> |
| </mx:Form> |
| </mx:Panel> |
| <mx:Panel label="Credit Card Info"> |
| <mx:Form> |
| <mx:FormItem label="Type:"> |
| <mx:ComboBox> |
| <mx:dataProvider> |
| <fx:String>Visa</fx:String> |
| <fx:String>MasterCard</fx:String> |
| <fx:String>American Express</fx:String> |
| </mx:dataProvider> |
| </mx:ComboBox> |
| </mx:FormItem> |
| <mx:FormItem label="Number:"> |
| <mx:TextInput id="ccnumber"/> |
| </mx:FormItem> |
| </mx:Form> |
| </mx:Panel> |
| <mx:Panel label="Check Out"> |
| <mx:TextArea id="ta2" text="You must agree to all the following conditions..."/> |
| <mx:CheckBox label="Agree"/> |
| </mx:Panel> |
| </mx:TabNavigator> |
| <mx:TextArea id="ta1" width="580" height="400"/> |
| |
| </s:Application></pre> |
| |
| <p>When you click on the second panel, you trigger a <samp class="codeph">change</samp> event, |
| which causes Flash Player to call the <samp class="codeph">updateURL()</samp> method. |
| This method calls the <samp class="codeph">setFragment()</samp> method that |
| changes the URL in the browser’s address bar. If you cycle through |
| the panels, the URL in the browser’s address bar will change from |
| this:</p> |
| |
| <pre class="codeblock"> http://localhost:8100/devapps/code/deeplinking/FragmentExample.html#panel=1</pre> |
| |
| <p>To this:</p> |
| |
| <pre class="codeblock"> http://localhost:8100/devapps/code/deeplinking/FragmentExample.html#panel=2</pre> |
| |
| <p>And then, to this:</p> |
| |
| <pre class="codeblock"> http://localhost:8100/devapps/code/deeplinking/FragmentExample.html#panel=0</pre> |
| |
| <p>Each time the application changes the URL in the browser’s address |
| bar, Flash Player dispatches an <samp class="codeph">applicationURLChange</samp> event. |
| This example logs the previous and current URLs, which are properties |
| of this event object.</p> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested2" id="WS2db454920e96a9e51e63e3d11c0bf64e50-7ffb_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf64e50-7ffb_verapache"><!-- --></a> |
| <h3 class="topictitle3">Parsing the URL</h3> |
| |
| |
| <div> |
| <p>Changing the URL in the browser’s address bar does not |
| necessarily provide you with deep linking functionality. For example, |
| loading a bookmarked URL that specifies the panel would not start |
| the application on that panel. You must add code to the application |
| that parses the URL so that the application’s state reflects the |
| URL. </p> |
| |
| <p>In this case, you add a listener for the <samp class="codeph">browserURLChange</samp> event. |
| In that listener, you parse the URL and set the application state |
| according to the results. For example, if the URL includes a fragment |
| such as <samp class="codeph">panel=2</samp>, then you write code that sets |
| the current panel with the selected index of 2.</p> |
| |
| <p>The <samp class="codeph">browserURLChange</samp> event is not triggered |
| when the application first loads. As a result, you must also check |
| the URL on startup with the application’s <samp class="codeph">creationComplete</samp> event, |
| and trigger the parsing before the application finishes being rendered |
| on the screen. </p> |
| |
| <p>The following is a simple example that sets the value of the |
| Accordion’s <samp class="codeph">selectedIndex</samp> property to the value |
| of the <samp class="codeph">index</samp> fragment in the URL. You request this |
| application by setting the value of the <samp class="codeph">index</samp> fragment |
| on the URL, as the following example shows:</p> |
| |
| <pre class="codeblock"> http://www.myurl.com/InitFrag.html#index=1</pre> |
| |
| <p>When the application starts up, the Accordion is opened to the |
| panel corresponding to the value of the <samp class="codeph">index</samp> fragment:</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/InitFrag.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" |
| creationComplete="init(event);"> |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.managers.BrowserManager; |
| import mx.managers.IBrowserManager; |
| import mx.events.BrowserChangeEvent; |
| import mx.utils.URLUtil; |
| |
| private var bm:IBrowserManager; |
| private function init(e:Event):void { |
| bm = BrowserManager.getInstance(); |
| bm.init("", "Welcome!"); |
| parseURL(e); |
| } |
| |
| [Bindable] |
| private var indexFromURL:int; |
| private function parseURL(e:Event):void { |
| var o:Object = URLUtil.stringToObject(bm.fragment); |
| indexFromURL = o.index; |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:Accordion selectedIndex="{indexFromURL}"> |
| <mx:VBox label="Panel 1"> |
| <mx:Label text="Accordion container panel 1"/> |
| </mx:VBox> |
| <mx:VBox label="Panel 2"> |
| <mx:Label text="Accordion container panel 2"/> |
| </mx:VBox> |
| <mx:VBox label="Panel 3"> |
| <mx:Label text="Accordion container panel 3"/> |
| </mx:VBox> |
| </mx:Accordion> |
| </s:Application></pre> |
| |
| <p>The following example expands on the example from <a href="flx_deep_linking_dl.html#WS2db454920e96a9e51e63e3d11c0bf69084-7cbc_verapache">Updating |
| the URL</a>. It reads the URL on startup, and opens the application |
| to the first, second, or third panel, depending on the value of |
| the <samp class="codeph">panel</samp> fragment. It also sets the title of the |
| HTML page according to which panel is opened.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/TabNavExample.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" |
| creationComplete="initApp()" |
| height="250" |
| width="500"> |
| |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.events.BrowserChangeEvent; |
| import mx.managers.IBrowserManager; |
| import mx.managers.BrowserManager; |
| import mx.utils.URLUtil; |
| |
| public var browserManager:IBrowserManager; |
| |
| private function initApp():void { |
| browserManager = BrowserManager.getInstance(); |
| browserManager.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE, parseURL); |
| browserManager.init("", "Shipping"); |
| } |
| private var parsing:Boolean = false; |
| private function parseURL(event:Event):void { |
| parsing = true; |
| |
| var o:Object = URLUtil.stringToObject(browserManager.fragment); |
| if (o.view == undefined) |
| o.view = 0; |
| tn.selectedIndex = o.view; |
| browserManager.setTitle((tn.selectedIndex == 0) ? "Shipping" : "Receiving"); |
| tn.validateNow(); |
| |
| var details:Boolean = o.details == true; |
| if (tn.selectedIndex == 0) |
| shipDetails.selected = details; |
| else |
| recvDetails.selected = details; |
| parsing = false; |
| } |
| |
| private function updateURL():void { |
| if (!parsing) |
| callLater(actuallyUpdateURL); |
| } |
| |
| private function actuallyUpdateURL():void { |
| var o:Object = {}; |
| var t:String = ""; |
| |
| if (tn.selectedIndex == 1) { |
| t = "Receiving"; |
| o.view = tn.selectedIndex; |
| if (recvDetails.selected) |
| o.details = true; |
| } else { |
| t = "Shipping"; |
| o.view = tn.selectedIndex; |
| if (shipDetails.selected) |
| o.details = true; |
| } |
| var s:String = URLUtil.objectToString(o); |
| browserManager.setFragment(s); |
| browserManager.setTitle(t); |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:TabNavigator id="tn" change="updateURL()" width="300"> |
| <mx:Panel label="Shipping"> |
| <mx:CheckBox id="shipDetails" label="Show Details" change="updateURL()" /> |
| </mx:Panel> |
| <mx:Panel label="Receiving"> |
| <mx:CheckBox id="recvDetails" label="Show Details" change="updateURL()" /> |
| </mx:Panel> |
| </mx:TabNavigator> |
| |
| </s:Application></pre> |
| |
| <p>This example has one major drawback: it does not “remember” the |
| state of the panel that is not in the current view. If you copy |
| the bookmark and open it in a new browser, the view that you were |
| last looking at, and the state of the CheckBox on that view, are |
| maintained. However, the CheckBox in the other view that was hidden |
| is reset to its original value (unchecked). There are several techniques |
| to solve this issue. For more information, see <a href="flx_deep_linking_dl.html#WS2db454920e96a9e51e63e3d11c0bf69084-7cbb_verapache">Using |
| deep linking with navigator containers</a>.</p> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested2" id="WS2db454920e96a9e51e63e3d11c0bf69084-7cbd_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7cbd_verapache"><!-- --></a> |
| <h3 class="topictitle3">About BrowserManager events</h3> |
| |
| |
| <div> |
| <p>The BrowserManager triggers the following types of BrowserChangeEvent events:</p> |
| |
| <ul> |
| <li> |
| <p> |
| <samp class="codeph">applicationURLChange</samp> |
| </p> |
| |
| </li> |
| |
| <li> |
| <p> |
| <samp class="codeph">browserURLChange</samp> |
| </p> |
| |
| </li> |
| |
| <li> |
| <p> |
| <samp class="codeph">urlChange</samp> |
| </p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>These changes can be triggered by the following actions:</p> |
| |
| <ul> |
| <li> |
| <p>URL is changed programmatically, such as with the BrowserManager’s <samp class="codeph">setFragment()</samp> method |
| (<samp class="codeph">applicationURLChange</samp>)</p> |
| |
| </li> |
| |
| <li> |
| <p>User clicks the Forward or Back button (<samp class="codeph">browserURLChange</samp>)</p> |
| |
| </li> |
| |
| <li> |
| <p>User changes the URL and clicks Enter or Go (<samp class="codeph">browserURLChange</samp>)</p> |
| |
| </li> |
| |
| </ul> |
| |
| <p>The <samp class="codeph">urlChange</samp> event is triggered whenever either <samp class="codeph">applicationURLChange</samp> or <samp class="codeph">browserURLChange</samp> events |
| are triggered.</p> |
| |
| <p>The following example shows the properties of the change events |
| in a DataGrid control. You can trigger an <samp class="codeph">applicationURLChange</samp> event |
| by selecting a new value in the ComboBox control. You can trigger |
| a <samp class="codeph">browserURLChange</samp> event by using the Forward and |
| Back buttons in your browser. In both cases, you also trigger a <samp class="codeph">urlChange</samp> event.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/URLChangeLogger.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" |
| creationComplete="init();"> |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.managers.BrowserManager; |
| import mx.managers.IBrowserManager; |
| import mx.events.BrowserChangeEvent; |
| |
| private var bm:IBrowserManager; |
| |
| private function init():void { |
| bm = BrowserManager.getInstance(); |
| |
| bm.addEventListener(BrowserChangeEvent.APPLICATION_URL_CHANGE, doEvent); |
| bm.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE, doEvent); |
| bm.addEventListener(BrowserChangeEvent.URL_CHANGE, doEvent); |
| |
| bm.init("", "Base title"); |
| } |
| |
| public function doEvent(evt:BrowserChangeEvent):void { |
| eventDG.dataProvider.addItem(evt); |
| } |
| ]]> |
| </fx:Script> |
| |
| <fx:Declarations> |
| <fx:Array id="dp"> |
| <fx:Object label="one"/> |
| <fx:Object label="two"/> |
| <fx:Object label="three"/> |
| </fx:Array> |
| </fx:Declarations> |
| |
| <mx:ComboBox id="cb" dataProvider="{dp}" |
| change="bm.setFragment('selectedItem=' + cb.selectedItem.label);"/> |
| |
| <mx:DataGrid id="eventDG" |
| dataProvider="[]" |
| width="100%" |
| variableRowHeight="true" |
| wordWrap="true" |
| height="500"/> |
| </s:Application></pre> |
| |
| <p>Changing the URL fragments in the browser’s address bar triggers |
| a <samp class="codeph">browserURLChange</samp> event but does not trigger a |
| page reload in FireFox or Internet Explorer 7. In Internet Explorer |
| version 6 and earlier, changing the part of the address that is |
| to the right of the pound sign (“#”) <em>does</em> trigger a page reload.</p> |
| |
| </div> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf64e50-7fff_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf64e50-7fff_verapache"><!-- --></a> |
| <h2 class="topictitle2">Setting the title of the HTML wrapper</h2> |
| |
| |
| <div> |
| <p>You can use the BrowserManager’s <samp class="codeph">setTitle()</samp> method |
| to set the title in your HTML wrapper. This shows up as the name |
| of the web page in the title bar of the browser. When you first |
| initialize the BrowserManager, you set the value of the title in |
| the second parameter to the <samp class="codeph">init()</samp> method.</p> |
| |
| <p>The following example sets the initial value of the title to |
| “Welcome”. It then changes the title depending on the name and hometown |
| that you enter in the TextInput fields.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/TitleManipulationExample.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" |
| creationComplete="init();"> |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.managers.BrowserManager; |
| import mx.managers.IBrowserManager; |
| import mx.events.BrowserChangeEvent; |
| |
| private var bm:IBrowserManager; |
| |
| private function init():void { |
| bm = BrowserManager.getInstance(); |
| bm.init("", "Welcome!"); |
| } |
| |
| public function updateTitle(e:Event):void { |
| bm.setTitle("Welcome " + ti1.text + " from " + ti2.text + "!"); |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:Form> |
| <mx:FormItem label="Name:"> |
| <mx:TextInput id="ti1"/> |
| </mx:FormItem> |
| <mx:FormItem label="Hometown:"> |
| <mx:TextInput id="ti2"/> |
| </mx:FormItem> |
| <mx:Button id="b1" click="updateTitle(event)" label="Submit"/> |
| </mx:Form> |
| |
| </s:Application></pre> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf69084-7e9c_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7e9c_verapache"><!-- --></a> |
| <h2 class="topictitle2">Passing request data with URL fragments</h2> |
| |
| |
| <div> |
| <p>In applications, there are several ways to pass values |
| to an application with the URL. You can convert query string parameters |
| to <samp class="codeph">flashVars</samp> variables or you can append them to |
| the SWF file’s URL (for example, MyApp.swf?value1=x&value2=y). |
| You do both of these things in the HTML wrapper that embeds the |
| application. To access the values in your application, you then |
| use the <samp class="codeph">FlexGlobals.topLevelApplication.application</samp> object. |
| For more information on passing <samp class="codeph">flashVars</samp> variables, |
| see <a href="flx_passingarguments_pa.html#WS2db454920e96a9e51e63e3d11c0bf69084-7f5e_verapache">Passing request |
| data with flashVars properties</a>.</p> |
| |
| <p>The BrowserManager also provides a method of accessing values |
| from a URL inside your application. You do this by accessing the |
| URL fragments that deep linking uses. By default, you can append |
| any Strings after the pound sign (“#”) in the URL and be able to |
| access them as semi-colon-separated name/value pairs. For example, |
| with a URL like the following, you can access the <samp class="codeph">firstName</samp> and <samp class="codeph">lastName</samp> values |
| once you convert the fragment to an object:</p> |
| |
| <pre class="codeblock"> http://www.mydomain.com/MyApp.html#firstName=Nick;lastName=Danger</pre> |
| |
| <p>To convert these parameters to an object, you get the URL and |
| then use the URLUtil class’s <samp class="codeph">stringToObject()</samp> method. |
| You then access <samp class="codeph">firstName</samp> and <samp class="codeph">lastName</samp> as |
| properties on that new object.</p> |
| |
| <p>To make the URL more “URL-like”, you can separate the fragments |
| with an ampersand (“&”). When you convert the fragment to an |
| object with the <samp class="codeph">stringToObject()</samp> method, you specify |
| the new delimiter.</p> |
| |
| <p>The following example takes a URL that sets the value of the <samp class="codeph">firstName</samp> and <samp class="codeph">lastName</samp> query |
| string parameters in its fragment. It specifies an ampersand as the |
| delimiter.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/PassURLParamsAsFragments.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" |
| creationComplete="init(event);"> |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.managers.BrowserManager; |
| import mx.managers.IBrowserManager; |
| import mx.utils.URLUtil; |
| |
| private var bm:IBrowserManager; |
| [Bindable] |
| private var fName:String; |
| [Bindable] |
| private var lName:String; |
| private function init(e:Event):void { |
| bm = BrowserManager.getInstance(); |
| bm.init("", "Welcome!"); |
| |
| /* The following code will parse a URL that passes firstName and lastName as |
| query string parameters after the "#" sign; for example: |
| http://www.mydomain.com/MyApp.html#firstName=Nick&lastName=Danger */ |
| var o:Object = URLUtil.stringToObject(bm.fragment, "&"); |
| fName = o.firstName; |
| lName = o.lastName; |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:Form> |
| <mx:FormItem label="First name:"> |
| <mx:Label id="ti1" text="{fName}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Last name:"> |
| <mx:Label id="ti2" text="{lName}"/> |
| </mx:FormItem> |
| </mx:Form> |
| |
| </s:Application></pre> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf69084-7cbb_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf69084-7cbb_verapache"><!-- --></a> |
| <h2 class="topictitle2">Using deep linking with navigator containers</h2> |
| |
| |
| <div> |
| <p>A common use case is for a user to start filling out a |
| form, and then either bookmark the form for later use, or send the |
| URL to someone else to take a look at the values. In many cases, |
| forms are implemented as navigator containers such as TabNavigator |
| and Accordion containers. </p> |
| |
| <p>This can be a problem for deep linking because you might try |
| to read properties on controls that are in views that have not yet |
| been viewed (and therefore, the controls have not yet been instantiated). |
| One possible solution is to disable deferred instantiation, and |
| set the value of the container’s <samp class="codeph">creationPolicy</samp> property |
| to <samp class="codeph">all</samp>. This instructs Flex to instantiate all |
| controls in all views at application startup, regardless of whether |
| they can be viewed initially or not. This is not a recommended solution |
| because it creates additional overhead when the application starts, |
| and can end up using unnecessary amounts of processor time and memory. |
| Depending on the complexity of the user interface, setting the value |
| of the <samp class="codeph">creationPolicy</samp> property to <samp class="codeph">all</samp> can |
| seriously degrade performance of your application and detract from |
| a positive user experience. For more information about creation |
| policies, see <a href="flx_layoutperformance_lp.html#WS2db454920e96a9e51e63e3d11c0bf69084-7cb8_verapache">About |
| the creationPolicy property</a>.</p> |
| |
| <p>A technique that does not rely on deferred instantiation is to |
| bind the values of the properties you want to maintain state on |
| to the values in the URL. Controls that have not yet been created |
| apply these values when they are created. This has the benefit of |
| keeping the URL and the application’s state in sync with each other, without |
| requiring that a view’s controls are first created. </p> |
| |
| <p>You do this by setting up variables that are mapped to the URL’s |
| values at start up and then kept in sync with the application as |
| the user interacts with it. When the application starts up, you |
| set the value of the bound property to the value taken from the |
| URL (if there is one). Then, whenever the application’s state changes (such |
| as when the user navigates to a new panel or clicks on a check box), |
| you update the bound property to the new value. You also update |
| the value of the property’s fragment in the URL.</p> |
| |
| <p>By doing this, though, you must wrap your variable assignments |
| with try/catch blocks because you will attempt to set the values |
| of variables with properties of components that might not yet have |
| been instantiated. This ensures that assignment errors are caught |
| rather than thrown as run-time errors. </p> |
| |
| <p>The following example uses a TabNavigator container to help the |
| user through a payment process. Whenever the user navigates to a |
| new panel, changes the value in a TextInput control, or checks the |
| CheckBox control, the URL and the bound properties are updated. |
| At any time, you can bookmark the URL, close your browser, and then |
| come back to the application at a later time. The values of all |
| the properties are maintained in the bookmarked URL.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/ComplexMultiPanelExample.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" |
| creationComplete="init();parseURL(event)"> |
| |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.events.BrowserChangeEvent; |
| import mx.managers.IBrowserManager; |
| import mx.managers.BrowserManager; |
| import mx.utils.URLUtil; |
| |
| private var bm:IBrowserManager; |
| |
| [Bindable] |
| private var agreeBoxFromURL:Boolean; |
| [Bindable] |
| private var personNameFromURL:String; |
| [Bindable] |
| private var hometownFromURL:String; |
| [Bindable] |
| private var cctypeFromURL:int; |
| [Bindable] |
| private var ccnumberFromURL:String; |
| |
| private function init():void { |
| bm = BrowserManager.getInstance(); |
| bm.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE, parseURL); |
| bm.init("", "Welcome!"); |
| } |
| |
| /* This method is called once when application starts up. It is also |
| called when the browser's address bar changes, either due to user action |
| or user navigation with the browser's Forward and Back buttons. */ |
| private function parseURL(event:Event):void { |
| |
| var o:Object = URLUtil.stringToObject(bm.fragment, "&"); |
| if (o.panel == undefined) |
| o.panel = 0; |
| |
| tn.selectedIndex = o.panel; |
| tn.validateNow(); |
| |
| personNameFromURL = o.personName; |
| hometownFromURL = o.hometown; |
| ccnumberFromURL = o.ccnumber; |
| cctypeFromURL = o.cctype; |
| agreeBoxFromURL = o.agreeBox; |
| } |
| |
| public function updateTitle(e:Event):void { |
| l1.text += "updateTitle()\n"; |
| bm.setTitle("Welcome " + personName.text + " from " + hometown.text + "!"); |
| } |
| |
| private function updateURL():void { |
| /* Called when state changes in the application, such as when the panel changes, |
| or a checkbox is checked. |
| |
| You must wrap the following assignments in a try/catch block, otherwise the |
| application tries to access components that have not yet been created. |
| You can circumvent this by setting the container's creationPolicy to "all", |
| but that is not a good solution for performance reasons. */ |
| try { |
| personNameFromURL = personName.text; |
| hometownFromURL = hometown.text; |
| ccnumberFromURL = ccnumber.text; |
| cctypeFromURL = cctype.selectedIndex; |
| agreeBoxFromURL = agreeBox.selected; |
| } catch (e:Error) { |
| } |
| |
| var o:Object = {}; |
| |
| try { |
| o.panel = tn.selectedIndex; |
| o.personName = personName.text; |
| o.hometown = hometown.text; |
| o.ccnumber = ccnumber.text; |
| o.cctype = cctype.selectedIndex; |
| o.agreeBox = agreeBox.selected; |
| } catch (e:Error) { |
| } finally { |
| var s:String = URLUtil.objectToString(o, "&"); |
| bm.setFragment(s); |
| } |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:TabNavigator id="tn" width="300" change="updateURL()"> |
| <mx:Panel label="Personal Data"> |
| <mx:Form> |
| <mx:FormItem label="Name:"> |
| <mx:TextInput id="personName" |
| text="{personNameFromURL}" |
| focusOut="updateURL()" |
| enter="updateURL()"/> |
| </mx:FormItem> |
| <mx:FormItem label="Hometown:"> |
| <mx:TextInput id="hometown" |
| text="{hometownFromURL}" |
| focusOut="updateURL()" |
| enter="updateURL()"/> |
| </mx:FormItem> |
| <mx:Button id="b1" click="updateTitle(event)" label="Submit"/> |
| </mx:Form> |
| </mx:Panel> |
| |
| <mx:Panel label="Credit Card Info"> |
| <mx:Form> |
| <mx:FormItem label="Type:"> |
| <mx:ComboBox id="cctype" |
| change="updateURL()" |
| selectedIndex="{cctypeFromURL}"> |
| <mx:dataProvider> |
| <fx:String>Visa</fx:String> |
| <fx:String>MasterCard</fx:String> |
| <fx:String>American Express</fx:String> |
| </mx:dataProvider> |
| </mx:ComboBox> |
| </mx:FormItem> |
| <mx:FormItem label="Number:"> |
| <mx:TextInput id="ccnumber" |
| text="{ccnumberFromURL}" |
| focusOut="updateURL()" |
| enter="updateURL()"/> |
| </mx:FormItem> |
| </mx:Form> |
| </mx:Panel> |
| |
| <mx:Panel label="Check Out"> |
| <mx:TextArea id="ta2" text="You must agree to all the following conditions..."/> |
| <mx:CheckBox id="agreeBox" |
| label="Agree" |
| selected="{agreeBoxFromURL}" |
| click="updateURL()"/> |
| </mx:Panel> |
| </mx:TabNavigator> |
| <mx:TextArea id="l1" height="400" width="300"/> |
| |
| </s:Application></pre> |
| |
| <p>Rather than update the values of the bindable variables in the <samp class="codeph">updateURL()</samp> method, |
| you can also set their values in the event handlers. For example, |
| when the user changes the value of the TextInput control, you can |
| use the <samp class="codeph">focusOut</samp> and <samp class="codeph">enter</samp> event |
| handlers to set the value of the <samp class="codeph">hometownFromURL</samp> property:</p> |
| |
| <pre class="codeblock"> <mx:TextInput id="hometown" |
| text="{hometownFromURL}" |
| focusOut="hometownFromURL=hometown.text;updateURL()" |
| enter="hometownFromURL=hometown.text;updateURL()" /></pre> |
| |
| <p>In this example, the controls in the view update the model when |
| the controls’ properties change. This helps enforce a cleaner separation |
| between the model and the view.</p> |
| |
| </div> |
| |
| </div> |
| |
| <div class="nested1" id="WS2db454920e96a9e51e63e3d11c0bf64e50-7ff8_verapache"><a name="WS2db454920e96a9e51e63e3d11c0bf64e50-7ff8_verapache"><!-- --></a> |
| <h2 class="topictitle2">Accessing information about the |
| current URL</h2> |
| |
| |
| <div> |
| <p>In some cases, it is important to get information about |
| the current URL, such as the server name or the protocol that was |
| used to return the SWF’s wrapper. You do this by using the BrowserManager |
| and the URLUtil class.</p> |
| |
| <p>You use the BrowserManager to get the URL, using either the <samp class="codeph">url</samp>, <samp class="codeph">fragment</samp>, or <samp class="codeph">base</samp> properties. |
| You can then use convenience methods of the URLUtil class to parse |
| the URL. You can use these methods to extract the port, protocol, |
| and server name from the URL. You can also use the URLUtil class |
| to check if the protocol is secure or not.</p> |
| |
| <p>The following example uses the URLUtil and BrowserManager classes |
| to get information about the URL that was used to return the application.</p> |
| |
| <pre class="noswf"><?xml version="1.0" encoding="utf-8"?> |
| <!-- deeplinking/UseURLUtil.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" |
| creationComplete="initApp()" |
| height="250" width="500"> |
| |
| <s:layout> |
| <s:VerticalLayout/> |
| </s:layout> |
| |
| <fx:Script> |
| <![CDATA[ |
| import mx.utils.URLUtil; |
| import mx.managers.IBrowserManager; |
| import mx.managers.BrowserManager; |
| import mx.events.BrowserChangeEvent; |
| |
| public var browserManager:IBrowserManager; |
| |
| private function initApp():void { |
| browserManager = BrowserManager.getInstance(); |
| browserManager.addEventListener(BrowserChangeEvent.URL_CHANGE, showURLDetails); |
| browserManager.init("", "Welcome!"); |
| } |
| |
| [Bindable] |
| private var fullURL:String; |
| [Bindable] |
| private var baseURL:String; |
| [Bindable] |
| private var fragment:String; |
| [Bindable] |
| private var protocol:String; |
| [Bindable] |
| private var port:int; |
| [Bindable] |
| private var serverName:String; |
| [Bindable] |
| private var isSecure:Boolean; |
| [Bindable] |
| private var previousURL:String; |
| |
| private function showURLDetails(e:BrowserChangeEvent):void { |
| var url:String = browserManager.url; |
| baseURL = browserManager.base; |
| fragment = browserManager.fragment; |
| previousURL = e.lastURL; |
| fullURL = mx.utils.URLUtil.getFullURL(url, url); |
| port = mx.utils.URLUtil.getPort(url); |
| protocol = mx.utils.URLUtil.getProtocol(url); |
| serverName = mx.utils.URLUtil.getServerName(url); |
| isSecure = mx.utils.URLUtil.isHttpsURL(url); |
| } |
| ]]> |
| </fx:Script> |
| |
| <mx:Form> |
| <mx:FormItem label="Full URL:"> |
| <mx:Label text="{fullURL}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Base URL:"> |
| <mx:Label text="{baseURL}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Fragment:"> |
| <mx:Label text="{fragment}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Protocol:"> |
| <mx:Label text="{protocol}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Port:"> |
| <mx:Label text="{port}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Server name:"> |
| <mx:Label text="{serverName}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Is secure?:"> |
| <mx:Label text="{isSecure}"/> |
| </mx:FormItem> |
| <mx:FormItem label="Previous URL:"> |
| <mx:Label text="{previousURL}"/> |
| </mx:FormItem> |
| </mx:Form> |
| |
| </s:Application></pre> |
| |
| </div> |
| |
| <p>Adobe, Adobe AIR and Adobe Flash Player are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries and are used by permission from Adobe. No other license to the Adobe trademarks are granted.</p> |
| </div> |
| |
| |
| </body> |
| </html> |