Menu

Communicating Between Web and Astro

What you’ll learn and how you can apply it

By the end of this section, you’ll be able to:

And you'll be able to:

Why?

Creating a great Astro app usually involves tying together relevant parts of your mobile website with native components provided by Astro. In order for the app to feel like a true native app and not just a thin wrapper around your site, you will need to have the site communicate with the app. Astro provides two methods of communication: events and Remote Procedure Call (RPC) methods.

An event is one-way and the event subscriber cannot communicate a result back to the component that raised the event directly. An RPC method is a function that looks like a simple, local function call but results in a method being called in another part of the program entirely. Wikipedia has a good explanation of what an RPC method is in their Remote procedure call entry.

Deciding which one to use can be non-obvious but here are some guidelines for deciding between an event and an RPC method:

This can basically be summarized as: events are 1:n and RPC methods are 1:1.

You can find the API reference for events and RPC methods in the official Astro API documentation.

With that said, let's look in detail at how to use each communication style.

Custom Events

You can send events from app.js to JavaScript in a WebViewPlugin instance, and vice-versa. These events are produced and consumed with a simple on() and trigger() API, with some slight variation in how its done between web views themselves and app.js.

Subscribing to an Event

A page can be hosted in a WebViewPlugin or can subscribe to events triggered by app.js by calling Astro.on() as follows:

Astro.on('event name', (params) => {
    // Event handling code for the event named: 'event name'
})

Similarly you can subscribe to events generated from within a WebViewPlugin in app.js by subscribing to the event on the WebViewPlugin instance itself:

webViewPluginInstance.on('event name', (params) => {
    // Event handling code for the event named: 'event name'
})

In both cases the second parameter, a function, receives a single params argument containing any parameters that were sent with the event by the event publisher.

Triggering an Event

Now that you have subscribed to an event, we can look at how to trigger one. Remember that an event can be triggered from only one component, but can be subscribed to from any number of locations and components. As with event subscription, the method varies slightly between app.js and from within a WebViewPlugin page.

Triggering an event from JavaScript hosted by a WebViewPlugin is done using the Astro object as follows:

Astro.trigger('loadedImage', {
    url: 'http://www.example.com/img/a.jpg'
})

Similarly, triggering an event from app.js into JavaScript hosted by a WebViewPlugin uses the instance again:

webViewPluginInstance.trigger('loggedIn', {
    userName: "Steve"
})

Example

Let's tie this all together with an example. Let's say you have a WebViewPlugin that is loading a product page (product.html?id=2948293). You want to open a native cart modal when the user adds the product to their cart. Within the product.html page there is an Add to Cart button that makes an AJAX call to an endpoint to add the product to the cart. In the AJAX completion function we'll put the following code in to raise an event notifying app.js that an item was added to the cart.

WebViewPlugin - product.html

// Use POST to add item to cart
$.post('actions/add-to-cart', { 'product_id': '2948293' }, (data) => {
    // … AJAX completion code
    Astro.trigger('added-to-cart', {
        productId: data.product_id
    })
})

Now in app.js we can listen for this event and open a ModalViewPlugin that hosts the cart page.

app.js

webView.on('added-to-cart', (params) => {
    // Refresh the cart page and show the modal.
    cartWebView.navigate('cart.html')
    modalView.show()
})

RPC Methods

Working with RPC methods is a two-step process. First, a component needs to register an RPC method. This makes it available to the rest of the app (think of it as "exporting" a function to the rest of the app). Once it's been registered, any component can then ask Astro to build a proxy function to that RPC method and call it. We'll look at RPC method registration first.

Registering an RPC Method

RPC methods must be registered before they can be called. You can register an RPC method from anywhere in an Astro app, including web content hosted in a

WebViewPlugin

Astro.registerRpcMethod('open-cart', ['animated'], (res, animated) => {
    modalViewPlugin.show(animated)
    res.send(null, true)
})

We have now registered an RPC named 'open-cart' that takes one parameter: animated. The function we passed in is the actual function that will get called when some other component calls this RPC method.

The Response Object

RPC methods really become useful when you are able to return a result to the caller of your RPC method. You'll notice that the first parameter to the function we registered was an object named res. This is the "response" object and the first parameter to any RPC method is always the response object (even if your RPC method takes 0 arguments).

The response object has a single method on it: send(error, result). You use this method to communicate completion of the RPC method to the caller along with a result or error. You should only ever pass an error or result, never both!

RPC methods use a response object for returning the result as opposed to just taking the return value of the RPC method so that the RPC method can easily call other asynchronous methods (think AJAX calls, etc).

Calling an RPC Method

Now that we've registered an RPC method, we can call it. RPC methods look and act just like a regular JavaScript function. The only difference is that you have to ask Astro to create the RPC function for you before you can call it. Creating the RPC method is done using the Astro object.

// Generate the function
const openCartModal = Astro.jsRpcMethod('open-cart', ['animated'])

// Call the RPC method to open the cart modal (with animation)
openCartModal(true)

Let's look at each parameter passed to jsRpcMethod().

The jsRpcMethod() returns a regular JavaScript function that you can use to call the remote procedure. The returned function accepts the same number of parameters as you listed in the parameter name array when you called jsRpcMethod().

Example

Let's tie this all together by creating a simple RPC method that calculates the taxes on two numbers together.

app.js

Astro.registerRpcMethod('calculate-taxes', [
        'subtotal',
        'includeProvincialTaxes'
    ], (res, subtotal, includeProvincialTaxes) => {
        let taxRate = 0.07;

        if (includeProvincialTaxes) {
            taxRate += 0.06;
        }

        res.send(null, subtotal * taxRate);
    }
)

product.html

// item added to cart, let's update the Taxes and Total labels
const calculateTaxes = Astro.jsRpcMethod('calculate-taxes',
    ['subtotal', 'includeProvincialTaxes'])

const taxes = await calculateTaxes(subtotal,
    false /* don't include provincial taxes */
);

$('taxes').text('$' + taxes)

Summary

In this section you learned about two methods to communicate between components of an Astro app. Events allow you to notify any number of listeners (subscribers) about some event that occurred. You can pass along data about the event in the params object. RPC methods allow you to export a function from one component and then call it from any other component in the app. This is useful when you want to invoke some native/app.js action from within a webpage. Good Astro apps utilize both of these methods to make the app work and feel like a truly native app.