Статьи

Взаимодействие между Flex и Javascript

Когда вы думаете о приложении Flex, о чем вы думаете? Приложение, которое заполняет весь экран и обрабатывает все взаимодействие с пользователем? Это то, что я привык думать. , , пока я не провел некоторое время, используя Пандору. Для тех из вас, кто не посещал веб-сайт Pandora, это веб-сайт, который позволяет вам ввести любимого музыкального исполнителя или название песни, чтобы он мог составить список воспроизведения для многих исполнителей, которые, по вашему мнению, вам могут понравиться. Служба может стать спасением для любого, кто, как и я, застрял, работая на ферме.

Просматривая сайт, я заметил часть функциональности, которая имела большой смысл. После каждых нескольких треков реклама будет меняться. Теперь большинство людей не будут думать об этом как о большом билете, но когда вы думаете, что приложение flash или Flex предназначено для предоставления вам всего вашего контента без необходимости загрузки новой страницы, это действительно большая проблема. В наше время, когда реклама управляет Интернетом, и все оценивается по кликам и показам, обновление вашей рекламы на странице имеет решающее значение. Что больше всего выделялось в рекламных объявлениях, так это то, что рекламные объявления как в приложении проигрывателя, так и в оболочке HTML приложения изменялись одновременно на одну и ту же рекламную кампанию.

Тогда я понял, что реклама страницы диктуется самим игроком. Поскольку я обычно думал о приложении Flex как о целой странице, я никогда не думал о силе, которой можно обладать при ширине всего лишь нескольких сотен пикселей.

Объединение Flex с Javascript дает вам полный контроль над всей страницей, а объединение этого с библиотекой jQuery позволяет легко манипулировать страницей и добавлять эффекты и функциональность. Доступ к этой функциональности лежит в том, что я люблю называть категорией «Flex One-Liners» — это категория функций во Flex, которые занимают только одну строку кода. Очевидно, вы захотите добавить дополнительные строки кода для обработки ошибок и проверки данных, но мозги этих функций — только одна строка.

Flex разговаривает с Javascript

Для взаимодействия с функциональными возможностями вне приложения Flex вам необходимо использовать класс ExternalInterface. Этот класс позволяет вам вызывать функции, которые доступны для главной страницы (оболочки) приложения. Если вы можете вызвать функцию из оболочки, вы можете вызвать ее из приложения Flex. Звонить легко; просто укажите функцию для вызова и затем передайте аргументы, вот так:

ExternalInterface.call("remoteFunction",arg1,arg2);

В этом примере мы вызываем функцию Javascript с именем remoteFunction и передаем ей значения arg1 и arg2, которые являются переменными в основном приложении. Чтобы использовать эти данные на главной HTML-странице, у нас должна быть функция Javascript, подобная этой:

function remoteFunction(val1,val2){
alert('Received data: ' + val1 + ' and: ' + val2);
}

Это кажется довольно простым, верно? Ну, на самом деле, это так просто. Передача ваших аргументов таким способом может показаться не такой эффективной, как вы привыкли. Возможно, вы более привыкли передавать объекты, а не просто значения в свои функции. Используя класс ExternalInterface, вы можете передавать сложные объекты, если хотите. Вот более сложный пример:

Flex:

private function sendData():void{
var obj:Object = new Object();
obj.firstName = "Simon";
obj.lastName = "Free";
ExternalInterface.call("receiveData", obj);
}

Javascript:

function receiveData(obj) {
alert("Hello " + obj.firstName + " " + obj.lastName + "!");
}

В этом примере функция sendData, которая существует в приложении Flex, создает объект с именами firstName и lastName и отправляет его в функцию Javascript в файле оболочки. Функция receiveData, которая существует в файле оболочки, затем получает данные и предупреждает пользователя о них. Хотя в этом примере мы все еще только пропускаем строки в объекте, вы можете передать более сложные данные, такие как массивы, если вам нужно.

Хорошо, теперь, когда мы увидели пример, давайте посмотрим, как мы могли бы объединить все это в реальном приложении. Поскольку нам нужно только продемонстрировать функциональность прямо сейчас, мы не будем создавать приложение, которое обязательно будет примером из реальной жизни. В этом примере мы создадим приложение Flex, которое позволяет пользователю выбирать цвет с помощью палитры цветов, а затем устанавливает фон страницы на этот цвет.

Сначала давайте посмотрим на код Flex:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="400" height="400">
<mx:Script>
<![CDATA[
private function sendData():void{
if (ExternalInterface.available) {
ExternalInterface.call("updateBackground",cPicker.selectedColor.toString(16));
} else {
trace('Wrapper not available');
}
}
]]>
</mx:Script>
<mx:Label text="Please select a color:" horizontalCenter="0" fontSize="15"/>
<mx:ColorPicker id="cPicker" change="sendData()" horizontalCenter="0" />
</mx:Application>

Как вы можете видеть в этом примере, у нас есть colorPicker, который настроен на вызов функции sendData при изменении цвета. Функция sendData делает две вещи. Первое, что он делает, это проверяет, доступны ли внешние интерфейсы. Эта проверка позволяет убедиться, что приложение Flex действительно вызывается со страницы, а не вызывается как SWF-файл напрямую. Если SWF-файл вызывается напрямую, то никакой оболочки не будет, и ни один из вызовов функций не будет успешным. Второе, что он делает, это вызывает функцию updateBackground для оболочки и передает выбранный цвет. Обратите внимание, что при использовании палитры цветов вы захотите преобразовать значение в формат, который может использовать CSS. Это делается путем вызова toString (16). Без этого вы просто получите целочисленное значение, а фон не будет обновляться.

Далее давайте посмотрим на Javascript:

function updateBackground(color){
$("body").css("background-color",color);
}

В этой чрезвычайно простой функции, используя jQuery, мы выбираем объект body и обновляем свойство CSS цвета фона с помощью цвета, передаваемого через Flex. Помните, что при создании функциональности в вашем приложении Flex, которому требуется доступ к Javascript, вы должны добавить его в файл index.template.html в папке html-template ваших проектов. Таким образом, при запуске вашего кода Javascript будет построен с Это.

Теперь, когда мы рассмотрели взаимодействие Flex с Javascript, мы должны рассмотреть обратную сторону. Как и Flex для связи с Javascript, Javascript для связи с Flex довольно прост. Как и прежде, я никогда особо не задумывался о необходимости этого, особенно когда размышлял об использовании в реальном мире. Лишь несколько месяцев назад я действительно увидел в этом пользу. В рамках рекламной кампании Apple Mac против ПК Apple разместила два флеш-баннера на главной странице CNN. Один баннер был помещен в заголовок, а другой — вниз по правой стороне страницы. Реклама включала ПК, поднимающийся по краям экрана и вводящий заголовок рекламы. Эта функциональность может быть реализована с помощью таймеров в рекламе,но что, если между двумя баннерами возникли проблемы с загрузкой, чтобы один баннер загружался раньше другого? Если бы использовались таймеры, флэш-фильмы были бы не синхронизированы и потеряли бы некоторые из вау-факторов. Создание функциональности, позволяющей одному флэш-фильму сигнализировать другому либо о готовности, либо о том, что произошло определенное событие, позволит вам синхронизировать оба фильма. Это может показаться немного сложным, но вы скоро увидите, что это не так. Давайте взглянем.но вы скоро увидите, что это не так. Давайте взглянем.но вы скоро увидите, что это не так. Давайте взглянем.

Javascript talking to Flex

Before we jump right into creating a page that has two Flex apps communicating with each other, let’s first look at how we can pass data from Javascript to a Flex app. Just like before, we will be using the ExternalInterface Class. When creating your Flex application you will need to add a callback handler so that your application will know what to do when a call is made. This is very similar to adding an event listener, but since you do not have anything to add an event listener to, you should use the ExternalInterface Class instead. To add the callback you would use the following code:

ExternalInterface.addCallback("dataPosted", dataPostedHandler);

Using the addCallback function of the ExternalInterface Class, you specify the function you are listening for, in this case dataPosted, and you specify the function to call within your application when the function has been called, in this case dataPostedHandler. The dataPostedHandler function will accept the arguments that were passed to the application and will handle them as necessary. Here is a look at the example dataPostedHandler function:

private function dataPostedHandler(txt:String):void{
trace(txt);
}

In this example, only one argument is passed to the function and then traced within the application. As before, multiple arguments, as well as complex data types, can be passed to the function. If using complex data types, you must remember to change the data type of the accepted argument within the argument declarations. Otherwise you will receive an error.

To make the call in Javascript you would use the same syntax you normally would when making a call on any object, Flex or otherwise.

private function dataPostedHandler(txt:String):void{
trace(txt);
}

In this example, we are calling a function, sendData, which calls the dataPosted function on the myApp object. The myApp object is the ID of the Flex application dictated in the object tag in the HTML. The function then passes in the value of a text input that is located in the HTML of the page.

While developing your application, rather than using the ID of the object, you could place the function in the index.template.html file and use one of the variables that is replaced when the code is generated. Doing that would result in a function that looks like this:

function sendData(){
${application}.dataPosted(document.getElementById("supText").value);
}

The ${application} variable is the same used for the ID of your generated object tag. If you use this method, you do not have to worry about naming issues.

Now that we have seen how the basic tags look, let’s delve into a real application. This application will demonstrate two Flex applications, each of which talks to the other and triggers events that the other will pick up. This is similar to the Mac vs. PC example, although it is less of a real world scenario. In this example we will have two arrows, one that will traverse the two Flex applications from left to right and one that will traverse the two Flex applications from right to left. The tricky thing here is that instead of creating 2 separate applications, we will create one application that will be placed twice on the page.

Rather than show you all the code, which has a lot of code specific to the example and not the communication, I am going to point out a few key pieces of functionality and let you review the rest of the code at your leisure.

The first important part of the application is in the init function. Within this function we decide which application we are, the left one (appID of 1) or the right one (appID of 2). This information is extracted from the FlashVars in the object tag in the wrapper. This value will be used for most of the decision making processes in the application. The second important part of the init function is adding the callback, which will listen for the receiveArrow call, to the ExternalInterface Class. Remember, no matter which version of the application it is, left or right, they both will have the receiveArrow function called on them, and both of them will dispatch the sendArrow externalInterface call. The left application dispatches the blue arrow and receives the red arrow, and the right application dispatches the red arrow and receives the blue arrow.

The next important part of the application is the startAction function. This function is called by clicking on the button at the bottom of the application. Depending on which application it is that you are clicking on, it will build the required arrow image, set up the parameters of the move object, and then start the move object. Once this has begun, it will start a timer which will periodically check the location of the image via the monitorPosition function. If at any time the monitorPosition function finds that the x co-ordinate has reached a certain point, it will make the externalReference call to the sendArrow function and pass in the ID for the other application. Notice that I chose to use a timer (rather than the tweenUpdate event of the Move object) to check the position of the image. The tweenUpdate event fires extremely fast, as there are many updates that occur. Because of this, it is very difficult to trap the initial time that the image has moved into the area at which you want to fire the command. As a result, if you use the tweenUpdate event to check the position of the image, the command will fire 10 — 20 times before you can flag it as having already occurred. The result is that as soon as one of the arrows gets to the other side of the application, 10 — 20 arrows suddenly appear on the other application. It may look comical, but it does not have the effect we are looking for. Using a timer gives us just enough of a gap between calls to register the first occurrence and block the rest.

The final piece of code I want to draw your attention to in the Flex application is the receiveArrowHandler function. This function is called via Javascript when one of the applications has an arrow that it wants to pass over. This function, depending on which application it is called by, fires the corresponding arrow to complete the effect of an arrow passing all the way from one application to another.

The necessary Javascript for the application pales in comparison to the lines of Flex code needed, which to be honest is not that many in the first place. The Javascript function needed on the wrapper page is:

function sendArrow(appID){
if(appID == 1){
${application}1.receiveArrow(appID);
}else{
${application}2.receiveArrow(appID);
}
}

As you can see, the function simply receives the appID from the calling application and then sends that appID to the other application, thus allowing for communication to occur between the two applications.

As you have seen in these examples, it is very easy to allow Flex applications to communicate with Javascript and Javascript to communicate with Flex applications. The biggest hurdle to get over is the vision that a Flex application by default must consume the entire page or that all its inputs must be received through the Flex application or remote calls. Once you have gotten over that hurdle and opened your mind to the different possibilities, you will be able to expand your applications into the realms of Pandora and Apple advertising campaigns. One final note of caution is that when creating applications that can have functionality called on them, do not allow those functions to access sensitive data. These calls can be hijacked, since everything that runs through Javascript can easily be monitored. Even passing a secret key between applications would not be a good form of security, because an application such as Firebug could easily find out the data. The best form of security for this form of communication is simply to not create functionality that can manipulate sensitive or important data.