Context and composers
Every view has one context: a Rust struct that holds the data the view needs. The handler builds it, and the view reads its fields. A view cannot use a field that the struct does not have.
Define the context
Derive Serialize so Propel can read the fields.
#[derive(Serialize)]
pub struct PostsIndex {
pub title: String,
pub posts: Vec<Post>,
}
Build it in a handler
The route is the view composer for this page: it collects the data and passes it to the view.
#[get("/posts")]
fn posts() -> PropelView {
PropelView::render("posts.index", &PostsIndex {
title: "Posts".into(),
posts: all_posts(),
})
}
Read it in the view
Fields are read by name, as in $title or $post.title. The $ is optional, and a loop variable such as post is read the same way.
@extends('layouts.app')
@section('content')
<h1>{{ $title }}</h1>
@forelse (post in posts)
<h2>{{ $post.title }}</h2>
@empty
<p>No posts yet.</p>
@endforelse
@endsection
Shared data with Page
Some data is needed on every page, such as the sidebar and the previous and next links. The test app wraps each context in a Page, which adds that data next to the view's own fields.
PropelView::render("posts.index", &Page::new("/posts", PostsIndex {
title: "Posts".into(),
posts: all_posts(),
}))
The layout then reads the shared data like any other field:
@foreach (group in nav)
<p>{{ $group.heading }}</p>
@foreach (link in group.links)
<a href="{{ $link.href }}">{{ $link.label }}</a>
@endforeach
@endforeach
Status
The Page wrapper is part of the test app for now, not of the library. In Laravel terms, it is a view composer written by hand. Propel will offer a request guard for shared data later. A view composer that runs on every request is planned, but not built.
Propel