Generic Components in Seed, Component Trait?

Hello, I am just getting into Seed. I have previously used Yew, which is similar, but I like the thin macros of Seed better than the JSX style html! macro of Yew, so i thought I give it a shot.

My current app in yew uses generic components. In yew, you implement a Component trait for structs which should be convertible to a HTML view and should be mounted to the DOM. When I have a generic struct, I can still define all of its instances as Components:

struct Model<T> {
    state: T,
}
impl<T> Component for Model<T> {
    fn create( ... ) -> Self { ... }
    fn view(&self) -> Html { ... }
    fn update(&mut self, msg: Msg) -> ShouldRender { ... }
}

How would I do this in seed? For some reason the examples in this guide) don’t seem to work?
when I define free functions like this:

pub fn update<C: Currency>(msg: (), model: &mut TransactionView<C>, _orders: &mut impl Orders<()>) {
}
pub fn view<C: Currency>(model: &TransactionView<C>) -> impl View<()> {
    tr![class!{"transaction-row"},
        ...
    ]
}

And try to use it like this:

pub fn update<C: 'static + Currency>(msg: (), model: &mut TransactionsView<C>, _orders: &mut impl Orders<()>) {
}
pub fn view<C: 'static + Currency>(model: &TransactionsView<C>) -> impl View<()> {
    table![class!{"transaction-table"},
        caption![class!{"transaction-caption"},
            "Your Transactions"
        ],
        tr![class!{"transaction-row"},
            th![class!{"transaction-header"}, "Date" ],
            th![class!{"transaction-header"}, "Amount" ],
            th![class!{"transaction-header"}, "Partner" ],
            th![class!{"transaction-header"}, "Purposes" ],
        ],
        model.model.iter().map(|t| // t has type Transaction<C>
            div![
                TransactionView::from(t.clone())
            ]
        )
    ]
}

I get the error:

error[E0599]: no method named `update` found for struct `transaction::TransactionView<C>` in the current scope
  --> server/home/src/transactions.rs:51:13
   |
51 | /             div![
52 | |                 TransactionView::from(t.clone())
53 | |             ]
   | |_____________^ method not found in `transaction::TransactionView<C>`
   |
  ::: server/home/src/transaction.rs:11:1
   |
11 |   pub struct TransactionView<C: 'static + Currency> {
   |   ------------------------------------------------- method `update` not found for this
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following traits define an item `update`, perhaps you need to implement one of them:
           candidate #1: `seed::virtual_dom::UpdateEl`
           candidate #2: `itertools::Itertools`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

Although this might actually be related to the docs being outdated.

It also seems to be that the App::builder() function doesn’t can’t handle generic instances of update/view functions, as this code:

#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
    App::builder(budget::update::<Euro>, budget::view::<Euro>)
        .build_and_run()
}

causes these errors:

error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
  --> server/home/src/lib.rs:29:35
   |
29 |     App::builder(budget::update::<Euro>, budget::view::<Euro>)
   |                                   ^^^^ explicit generic argument not allowed

error[E0599]: no method named `build_and_run` found for struct `seed::AppBuilder<budget::Msg, budget::BudgetView<plans::currency::Euro>, impl seed::virtual_dom::View<budget::Msg>, seed::app::UndefinedGMsg, seed::app::builder::UndefinedInitAPI>` in the current scope
  --> server/home/src/lib.rs:30:10
   |
30 |         .build_and_run()
   |          ^^^^^^^^^^^^^ method not found in `seed::AppBuilder<budget::Msg, budget::BudgetView<plans::currency::Euro>, impl seed::virtual_dom::View<budget::Msg>, seed::app::UndefinedGMsg, seed::app::builder::UndefinedInitAPI>`

In total I think it would be very beneficial to introduce a trait for objects which should implement an update and view method, at least. Similar to the Component trait in the Yew framework. This would make it easier to debug components and would make it easier to define generic components. also it would allow for better error messages if a type does not implement the trait.

They should work, but it’s true that there were bigger changes so it’s possible we missed something. Also the changelog for unreleased 0.7.0 is pretty big so I plan to update seed-rs.org docs + architecture and fix examples.
You can always look at the examples in the Seed repo because they are basically integration tests for Seed and demonstrate multiple patterns and APIs. (master is pretty stable, tested and linted with pedantic clippy).

There were some attempts to design API for this kind of components (from contributors and I tried it, too). But trade-offs weren’t worth it - you would lose flexibility and instead of dumb function calls, central place for your business data and implementing only necessary functions / state, you would need to apply some patterns for state synchronization and inter-component communication. So we decided to use pure Elm architecture (TEA) instead.

However we know the biggest trade-off for TEA is boilerplate / wiring between modules. So we are exploring React-like hooks and they are already implemented in the standalone crate and we are waiting for the one important Rust nightly feature to stabilize - then the hooks will be integrated into Seed (together with typed “CSS-in-JS” for Seed).

I tried to rewrite your code snippets into a working example: https://github.com/MartinKavik/seed/blob/examples/transactions/examples/transactions/src/lib.rs (you can clone the branch, run cargo make start transactions from the root and open http://localhost:8000/ to play with it).
I hope it helps with the “mental shift” to TEA - I recommend to learn something about it on official Elm pages and keep in mind that components / hooks are on the way (you can try hooks on nightly - there is a beta tutorial for them - ask user rebo on chat for it).

Also let me know if I didn’t explain something clearly enough or you have more questions.
Cheers, M.

1 Like