Every view comes with stateful information housed on the renderer instance for us to utilize and display. State is how we'll pass information from our route handlers into the page itself. By default, AdonisJS provides many utilities via this state, and we can inspect it in a couple of different ways.
Inspecting State
First, one of these utilities is an inspect method which will plop the contents of its argument on our page. To access it, we can use EdgeJS's interpolation, initiated with double-curly braces.
@layout() <h1>Challenges</h1> {{ inspect(state) }} @endCopied!
- resources
- views
- pages
- challenges
- index.edge
Our state is housed in a variable called state. With the above, if we visit our page now, we'll see a list of all existing state utilities.
Although we can access state using the state variable, it's also directly accessible as well. So, as you can see inspect is listed as an item in our state, but we didn't need to do state.inspect() to use it.
Dumping State
Now, inspect is fantastic for simple things, but when inspecting a lot, it can be a bit much to weed through. An alternative option is to dump state. This, like inspect will plop what we're inspecting on the page, but this will instead make it collapsible so we can dig into specific properties as needed, making it easier to digest large items.
@layout() <h1>Challenges</h1> @dump(state) @endCopied!
- resources
- views
- pages
- challenges
- index.edge
Dump is what is referred to as a tag in EdgeJS, similar to how we're using our layout. Tags don't require interpolation to be accessed like traditional state; instead, they can be accessed by prefixing them with an at symbol (@). They must also be on a line of their own.
Passing Data to Views
Great, now when rendering inside our route handlers, we can pass additional state as the second argument to the render method. So, if we wanted to pass a list of challenges into the view, we can define the list and pass it in via an object as the second argument. This object will then get merged with the pre-existing state and be made accessible within our view.
router.get('/challenges', async (ctx) => { const challenges = [ { id: 1, text: 'Learn AdonisJS', points: 10 }, { id: 2, text: 'Learn EdgeJS', points: 5 }, { id: 3, text: 'Build an AdonisJS app', points: 20 }, ] return ctx.view.render('pages/challenges/index', { challenges }) })Copied!
- start
- routes.ts
Now, within our dumped output, we should see challenges as one of the items in our state!
The 3 Types of State in EdgeJS
Within EdgeJS, there are three different levels of state.
Render state - which we're using above, and is only available on the direct page being rendered
Local state - shared data accessible throughout the individual request's view instance
Global state - shared data accessible across all request view instances
To demonstrate this, let's switch our dump to an inspection of just our challenges. Additionally, let's also inspect our challenges from our layout component.
@layout() <h1>Challenges</h1> {{ inspect({ view: challenges }) }} @endCopied!
- resources
- views
- pages
- challenges
- index.edge
<!DOCTYPE html> <html lang="en-us"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title> AdonisJS - A fully featured web framework for Node.js </title> @vite(['resources/css/app.css', 'resources/js/app.js']) @stack('dumper') </head> <body> @include('partials/header') {{ inspect({ layout: challenges }) }} <main> @include('partials/flash_alerts') {{{ await $slots.main() }}} </main> </body> </html>Copied!
- resources
- views
- components
- layout.edge
With this, we can see our render state in action. It's accessible only in the direct page being rendered, not within our component's state. If, however, we switched this from render state to local state, we'll see that it's available in both.
router.get('/challenges', async (ctx) => { const challenges = [ { id: 1, text: 'Learn AdonisJS', points: 10 }, { id: 2, text: 'Learn EdgeJS', points: 5 }, { id: 3, text: 'Build an AdonisJS app', points: 20 }, ] ctx.view.share({ challenges }) return ctx.view.render('pages/challenges/index') })Copied!
- start
- routes.ts
The share method on our HttpContext's view is how we can add local state. This is helpful for:
Adding request-specific state in or outside of the route handler
Adding request-specific globals that are deeply accessible within our views
Global State
Unlike render and local state, global state is not request-specific. It's great for adding helpers or general things we'll need throughout EdgeJS. To add a global, we'll first want to create a new preload file so we can register the global prior to our application starting.
node ace make:preload globals # ❯ Do you want to register the preload file in .adonisrc.ts file? (y/N) › true # DONE: create start/globals.ts # DONE: update adonisrc.ts fileCopied!
When asked if we want to register the preload file, hit yes! This hooks the preload file into our app's lifecycle by adding it to the adonisrc.ts file's preloads array.
In addition to updating our adonisrc.ts file this will also give us a start/globals.ts file to define our globals within.
import edge from 'edge.js' edge.global('appName', 'Lets Learn AdonisJS 7')Copied!
- start
- globals.ts
To define a global, we just need to import edge from 'edge.js' and call the global method. The first argument is the property name, and the second is the value. The value could be a simple primitive, a function, or anything we need. Once set, the value is globally accessible across all rendered views in our application, so definitely don't share user-specific info this way! This is great for helpers and the like.
@layout() <h1>Challenges</h1> {{ inspect({ view: challenges, appName }) }} @endCopied!
- resources
- views
- pages
- challenges
- index.edge
<!DOCTYPE html> <html lang="en-us"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title> AdonisJS - A fully featured web framework for Node.js </title> @vite(['resources/css/app.css', 'resources/js/app.js']) @stack('dumper') </head> <body> @include('partials/header') {{ inspect({ layout: challenges, appName }) }} <main> @include('partials/flash_alerts') {{{ await $slots.main() }}} </main> </body> </html>Copied!
- resources
- views
- components
- layout.edge
Okay, I'm going to remove these inspects and change our challenges back to render state.
router.get('/challenges', async (ctx) => { const challenges = [ { id: 1, text: 'Learn AdonisJS', points: 10 }, { id: 2, text: 'Learn EdgeJS', points: 5 }, { id: 3, text: 'Build an AdonisJS app', points: 20 }, ] return ctx.view.render('pages/challenges/index', { challenges }) })Copied!
- start
- routes.ts