framesurge / perseus

A state-driven web development framework for Rust with full support for server-side rendering and static generation.
https://framesurge.sh/perseus/en-US
MIT License
2.17k stars 89 forks source link

Ergonomic Improvements for `perseus::template::Template` #57

Closed lukechu10 closed 3 years ago

lukechu10 commented 3 years ago

Is your feature request related to a problem? Please describe. The current way of defining a template is somewhat boilerplate heavy (taken from https://arctic-hen7.github.io/perseus/en-US/docs/0.3.x/templates/intro):

use perseus::Template;
use std::rc::Rc;
use sycamore::prelude::{component, template, GenericNode, Template as SycamoreTemplate};

#[component(AboutPage<G>)]
pub fn about_page() -> SycamoreTemplate<G> {
    template! {
        p { "About." }
    }
}

pub fn get_template<G: GenericNode>() -> Template<G> {
    Template::new("about")
        .template(Rc::new(|_| {
            template! {
                AboutPage()
            }
        }))
        .head(Rc::new(|_| {
            template! {
                title { "About Page | Perseus Example – Basic" }
            }
        }))
}

Describe the solution you'd like A simple way of slightly improving this would be to get rid of the Rc::news by accepting a impl Fn(Option<String>) instead.

Another possible solution would be to add the ability to set a component as the template function. Something like:

Template::new(...).component::<AboutPage<_>>()

Describe alternatives you've considered Leave it the way it is

arctic-hen7 commented 3 years ago

I like the impl Fn(...) solution, and I like getting rid of the template function even more, but the problem is automatic deserialization of properties (of which there may not even be any). I'll definitely do the first part, but I think we should keep this open as a tracking issue for possible solutions to the entire issue of the existence of a template function, because I agree with you that it's very unergonomic.

arctic-hen7 commented 3 years ago

Okay, Rcs are now gone completely and done behind the scenes! The next thing will be possible automatic serialization/deserialization I think, because that removes the need for a template function.

arctic-hen7 commented 3 years ago

I've added a template proc macro that allows users to define their templates with an extra annotation #[perseus::template(ComponentName)], and they can then avoid the closure in the .template() call, which is I think as close as we'll get to a .component() call.

arctic-hen7 commented 3 years ago

I've also now added a macro that will do serialization and deserialization of custom props automatically, which reduces the number of serde_json calls drastically (though it's still needed as a dependency when #[perseus::autoserde(...)] is used).

arctic-hen7 commented 3 years ago

Okay, I think the new macros are a pretty good solution to the problems of Perseus' ergonomics, so I'm going to close this issue. @lukechu10 please let me know if you think there's anything else in particular that should be done!

lukechu10 commented 3 years ago

Looks great!