Page & App Scope
To build Multi-Page Apps in Superblocks, it is important to understand how resources such as Frontend Variables, APIs, Timers, Events, and more are scoped. Scope enables you to keep your application cleanly organized, while also sharing and reusing resources and data across pages.
When you create a resource, you will choose whether this resource is created at the Page or App level. Resources can be created at either level via the Navigation menu or inline from any action menu.
Page vs App-level resources
APIs do not yet support app scope; an API is scoped to a page and can only be invoked from that page. To reuse the response of an API across pages, store the response in a frontend variable scoped to the application.
At a high-level, each resource in a Superblocks Application must belong to either a specific page or the app. Whether a resource is Page or App level dictates where the resource can be accessed, as well as how other resources are references.
Page level
Most resources in your app, such as components, frontend variables, APIs, timers, and custom events are created at the page level. These resources belong to one page and are only accessible from that page.
App level
Frontend variables, timers, and custom events can be created at the app-level, which means they are shared across the app and are accessible from any page in the application.
Frontend variables
App-level frontend variables are preserved as you switch between pages. These variables are useful for sharing authentication state, user data, cached API responses, or other global data across your application.
App-level frontend variables come with the same persistence options: Temporary
and Local Storage
.
Temporary
variables are preserved only for the current session. This means they are preserved as the user navigates between pages, but are lost when the user refreshes the page or closes the browser.Local Storage
variables are preserved across sessions. This means they are saved to the user's browser and are available even if the user closes the browser and returns later.
Timers
App-level timers are shared across all pages in your application. This is useful for creating global timers, such as a session timeout timer, that need to be active regardless of which page the user is on.
These timers can be started, stopped, and reset from any page in your application.
Custom events
App-level custom events are shared across all pages in your application. This is useful for sharing common frontend logic that can be triggered from any page. For example, you might have a custom event that triggers a global error message to be displayed and sets an app-level variable to track the error state.
Referencing page and app level resources
Scope determines both:
- Where a resource can be accessed
- How the resource is referenced
From page scope
From a specific page, you can access other resources that are part of the same page as well as resources at the app-level. For example, when writing some JavaScript code inside of a component property or page-level state variable, you can reference entities at the page or app-level.
What can be referenced from this scope? | How do I reference resources at this level? |
---|---|
Other page-level resources on this specific page | No prefix - Ex: {{ variable1.value }} refers to a page-level variable variable1 |
App-level resources | App. prefix - Ex: {{ App.variable1.value }} refers to an app-level variable variable1 |
From app scope
From the app-level, you can only reference other app-level resources. To reference an app-level resource from another app-level resource, you do not need to use a prefix. For example, {{ variable1.value }}
refers to an app-level variable variable1
.
What can be referenced from this scope? | How do I reference resources at this level? |
---|---|
App-level resources | No prefix - Ex: {{ variable1.value }} refers to an app-level variable variable1 |
Example - caching an API response using App State
Consider the case where I have a Customers and CustomerDetail page.
When a user navigates to the Customers page, I want to fetch a list of customers and cache this list so that when the user navigates to the CustomerDetail page, I don't need to re-fetch the details for a specific customer if they were already fetched on the Customers page.
This type of caching will avoid unnecessary API calls and improve the performance of my application.
On my Customers page, I can create an app-level frontend variable cachedCustomersObject
where I can store the list of customers I fetch from my API.
Now, I navigate to my getCustomers
API. This API queries my list of customers from the database.
I want to turn this list into an object keyed by Customer ID, so that I can easily look up the user's information by ID on the Customer Detail page. I add a RunJS response event handler to my API with the following code.
function arrayToObjectById(array) {
return array.reduce((obj, item) => {
obj[item.id] = item;
return obj;
}, {});
}
// Convert the array of customers to an object keyed by ID
const result = arrayToObjectById(getCustomers.response);
// Merge the new object with the existing fetchedCustomers object
const mergedObject = {
...App.cachedCustomersObject.value,
...result,
};
// Set the App state variable cachedCustomersObject to the value of the merged object
App.cachedCustomersObject.set(mergedObject);
Next, I'll go over to my CustomerDetail page. Here, I have an API getCurrentUser
that queries user info for the relevant customer, per the ID passed in the URL route.
I need to keep this API, since the user can navigate directly to the CustomerDetail page without visiting the Customers page first.
However, I can now update my API to check if the user's information is already in the App.cachedCustomersObject
object. If it is, I can simply return that cached information, rather than re-querying the database.
To implement more robust caching, you would likely want to consider a cache invalidation strategy, as well as a mechanism to update the cached data set when new data is fetched anywhere in the app.