Coder Social home page Coder Social logo

seed's Introduction

crates.io version crates.io downloads docs.rs


Seed is a Rust front-end framework for creating fast and reliable web apps with an Elm-like architecture.

  • completely written in Rust, including the templating system (e.g. div! macro).
  • built-in state management that is based on the Elm architecture.
  • clear and extensive documentation for Rust beginners and pros alike.
  • WebAssembly.

Why Seed?

Seed allows you to develop the front-end with all the benefits of Rust, meaning speed, safety, and too many more things to count.

The Seed templating system uses a macro syntax that makes Rustaceans feel right at home. This means linting, formatting, and commenting will work, and it's all in Rust. This is opposed to a JSX-like syntax that relies on IDE extensions to improve the developer experience.

Why not Seed?

Getting Started

To get started right away, we can use the quickstart template:

cargo install cargo-generate
cargo install trunk
cargo install wasm-bindgen-cli
cargo generate --git https://github.com/seed-rs/seed-quickstart.git --name seed-quickstart
cd seed-quickstart
trunk serve

If you get an error about wasm being linked against a different version of wasm-bindgen, just follow the suggestion to run cargo update -p wasm-bindgen. This will fix the linkings.

You should now see a working counter app in your browser at localhost:8080.

Getting into Seed

The Seed website and the library docs are the best way to learn about the functionalities of Seed.

The Seed examples are another good resource.

Trunk is the recommended application bundler for Seed. Seed projects are typically run with trunk serve instead of cargo run. You might also see cargo make start project_name in the examples. Going forward, we recommend using Trunk.

Seed Styles is a styling library for Seed to create global and scoped styles.

To use web APIs, there is web-sys which is a part of the wasm-bindgen project. wasm-bindgen is a dependency of Seed.

There are also two template repositories. However, they are not currently up to date.

FAQ

How stable is Seed?

As a framework, Seed is mostly feature-complete. You can build complete web apps in Seed. Projects built in Seed do use Rust stable. Being in Rust, it's easy to create robust, predictable programs.

What's next for Seed?

Seed is not maintained at the moment but if you want to see some features and bring a budget, feel free to contact us.

Documentation

Resources

Contributing

See CONTRIBUTING.md.

Supported By

See BACKERS.md.

seed's People

Contributors

akhilman avatar allan2 avatar alterionx avatar arn-the-long-beard avatar ben-ph avatar callumjhays avatar ctjhoa avatar david-oconnor avatar eopb avatar flosse avatar fosskers avatar glennsl avatar hgzimmerman avatar jesskfullwood avatar jgrund avatar klemens avatar martinkavik avatar msuesskraut avatar muhannadalrusayni avatar naufraghi avatar pauan avatar platy avatar sagiegurari avatar sapir avatar spersson avatar tatrix avatar tiberiusferreira avatar tkubicz avatar zakaluka avatar zimond avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

seed's Issues

Changelog

It would be cool to introduce a CHANGELOG.md :)

Access to rendered element

I'm trying to implement a drag&drop system where the user has a list of divs and they can reorder them by dragging them around. The div under the dragged element should be highlighted.

To identify the highlighted element, I'd like to compare the current drag position with each div's bounding box, retrieved with get_bounding_client_rect. This would be done inside view, to decide whether to apply the highlighting styles, and it would use the bounding box of the element rendered by the previous view call.

Of course, I can't just access the element in my view function. So I tried using did_mount. There I can retrieve the bounding box, but I can't save it in my model. I got around that by having an Rc<RefCell<...>> in the model and then modifying it in did_mount, but this doesn't seem the right way to do things.

In Elm, I had done this using Browser.Dom.getElement. I think I called it from my init function, and I think the Task was executed after view, and then again later due to some subscription event.

I now realize that I can use document.get_element_by_id at view time to get the previously rendered element (if any) by ID. This would probably be cleaner than the RefCell, but it seems like it would be nicer to keep the information inside my Model.

How should I solve this?

Routing and Redirection

I am trying to implement some simple login redirection for my web frontend using Seed: if the user navigates to a protected page but is not logged in, redirect to the login page (by pushing a route).

This feels like logic that ought to be in my model's update call. However, seed::push_route requires the application App<Msg, Model> object. Meanwhile, all the code examples seem to be capturing the App in the view() method, and using an event to trigger the route-pushing. How would I trigger this event programmatically? All the events (e.g., simple_ev(...)) seem to be related to user interaction, but I want my application to do this automatically. Some sort of "send event to myself" call would seem in order (e.g., a hypothetical seed::queue_message(...)).

I have additionally tried calling seed::window().location().assign(...), but this seems to lead to redirections loops--the browser refreshes/redirects itself over and over to the same page.

Any ideas on how to inspect the model and programmatically push a route?

Recommended way of triggering model events from outside of normal flow.

To explain this a bit better, I need to be able to trigger a model update event from a closure passed to JS land. Take the websockets example for reference. Currently, it is recommended to pass a clone of the App<_, _> to the various callbacks so that they can push updates to the system as needed.

However, this is complicated when the websocket goes down and a new one needs to be established. In such a case, we would need to have a reference to the App<_, _> somewhere in the model so that it can be accessed when a new Websocket needs to be built, which will require new callbacks to be attached to the new Ws.

Seems like we could accomplish this by emitting an event to the model which takes a clone of the app, which would then be stored somewhere on the model for later use. This creates a cycle ... and definitely feels dirty ... but is a path forward. Especially because the cycle doesn't matter due to the fact that the app will most likely be alive for the entire duration of the program.

Thoughts?

Proposal: AnimationFrame

Suggested Changes

  • Sync render loop with requestAnimationFrame
  • Add variant ForceRender (or RenderNow (?)) into enum ShouldRender
    • ForceRender ignores requestAnimationFrame and forces app render

Why

Main reason is performance, however there are some other reasons, see dev.opera.com/articles/better-performance-with-requestanimationframe/.

Frameworks which already use rAF:

  • Rust framework Sauron
  • Yew has it on its roadmap (yew/issues/149) and has it implemented as a Render service (I was using it with "hacked" Yew's update function and app seems to be more responsive and animations (dragging / resizing) smoother)
  • Elm
  • React
  • ...

Resolving Breaking Changes

There are no API breaking changes, but there can be some special cases where ForceRender should be called instead of Render.
See github.com/elm/virtual-dom/issues/107 (there are broken links, just replace elm-lang with elm in their urls)

Possible Implementation

Can be inspired by:

Next Steps

I'll try to implement it after we agree on a scope of changes. Thanks for opinions and counter-arguments.

Future Work

We can add hooks/events for animation frame - see Elm-Browser-Animation docs. It will allow us to create precise animations.

API docs fo 0.3.3 and Orders

Hello, I've tried to put TodoMVC outside of examples into my project, but Orders are not found, also on 0.3.2 docs I cannot find them as well. 0.3.3 docs are not built due to some usual nightly tooling issues. Could you tell me, what Orders is and how to use TodoMVC outsise of seed repo?

Infinite Reloads when using Query Strings

First, Thanks for this awesome project! This is my favorite Rust/Webassembly project so far.

Adding a ?something to (any?) seed site with routing causes infinite reloads. For example: changing https://seed-rs.org/guide/7 to https://seed-rs.org/guide/7?adwd causes it to reload forever. Tested on Chrome and Safari on MacOS.

This is a problem for me because I posted my site ( https://tiberiusferreira.github.io/gringoshouse ) on facebook and facebook rewrites it as https://tiberiusferreira.github.io/gringoshouse/?fbclid=IwAR0bd0aunkNfmsimtcOvpq0VaaEbbslx_5LvqQaHRpMNbf4q2Ibs8v9may8 so I had to disable routing all together.

Add an Effect(Model, Msg) option to Update enum

My initial view of this lib is "cool, Elm but in Rust!". However it has also elements of React (componentDidMount etc) which reduce the "purity" of the experience for some convenience, which is obviously a design tradeoff.

Now in Elm, the update function returns a Model and a Msg. The model is immediately rendered by the view function, the Msg (if any) is added back into the event loop and is handled the next time round. This means data and effects flow in the same direction all the time, which IMO makes it nice to reason about.

The update function is seed is a little different. It only returns a Model, or rather, an Update<Model>. Any effects from the message (i.e. creating more messages) are, as far as I can tell, supposed to be handled 'in place', by passing around the App and running App.update as and when it is necessary.

I hit a 'race condition' of sorts using this style. I made a login fetch request, and when the function returns I want to redirect to the Home route. When fetch resolves it runs something like

|login_data| app.update(Msg::LoggedIn(login_data, app.clone()))

This callback triggers a new message to be sent through the event loop, OK, fine. Then I handle it in the update function like

match msg {       
   LoggedIn(session, app) => {
        app.update(Msg::ChangePage(Route::Home));
        Model { session: Some(session), ..model }
    },
    // ....
}

Spot the bug? app.update runs immediately, redirecting to Home, then returns to the update function, which creates a new model with the previous Route, effectively taking me back to the login page. I could change the new model to point to Route::Home too, but I really want all routing to happen only through Msg::ChangePage as that handles url-updating as well.

Really what I want is the Elm behavior of just returning a variant Effect(Model, Msg) from my match statement, and have the framework handle it in order. The I won't have to pass the App around, also.

Thoughts?

Rendering issue with similar elements under different parents.

I've been running into an issue where I have a few views which have a somewhat similar layout.

When the view is changed, the child elements which are similar are not updated. It would seem that the diffing algorithm is not taking into account certain attributes during the diff. The exact issue is as follows:

  • two different parent elements with similar layout.
  • both have an input element. The placeholders are different and even their bound At::Values are different.
  • when switching between the views, the input elements are not updated.

I tried using different IDs on the parent elements which are being changed, in hopes that the diffing algorithm would see this as a fundamentally different element and invalidate all of its children, but I know there are lots of nuances here.

I have the code in a public repo, let me know if you need repro steps.

Trigger routing immediately upon load

I think in most contexts seed is used to create single page applications.
The whole app belongs to one real page but the "virtual" routing is usually done by using Location.hash.
With a page located at https://example.com/single-page seed should use URLs like
single-page#home and single-page#other instead of single-page/home and single-page/other.

Make the framework trigger and update immediately upon load (see #34 (comment))

Empty elements won't update even if the rendered view changes

Building on the quickstart repo, I tried to display part of the model on the page:

use seed::*;
use seed::prelude::*;

// Model

#[derive(Clone, Debug)]
struct Model {
	input: String
}

impl Default for Model {
	fn default() -> Self {
		Self {
			input: "".into() // <--- change this to any other string to make it work
		}
	}
}

// Update

#[derive(Clone, Debug)]
enum Msg {
	ChangeInput(String)
}
use self::Msg::*;

fn update(msg: Msg, model: Model) -> Model {
	match msg {
		ChangeInput(x) => Model { input: x }
	}
}

// View

/// The top-level component we pass to the virtual dom.
fn view(_state: seed::App<Msg, Model>, model: Model) -> El<Msg> {
	div![
		p![ model.input ],
		input![ attrs!{"type" => "text"; "placeholder" => "Enter input text.."}, input_ev("input", Msg::ChangeInput), model.input ]
	]
}


#[wasm_bindgen]
pub fn render() {
	// The final parameter is an optional routing map.
	seed::run(Model::default(), update, view, "main", None, None);
}

The problem seems to be that the paragraph isn't updated if it's empty at first. It works when the initial value isn't empty.

New Feature Expected: Decoupling Component

Hi,

I'm watching seed several month. I like it and hope it become the wide-using wasm webapp dev framework.

everything is fine, except the following:

I'd like to create a frontend component library, like [ng-bootstrap](https://ng-bootstrap.github.io/#/components/alert/examples), using seed. It's a difficult task!

Is it possible that seed provide REACT like component, which have global level MODEL and component level MODEL at the same time?

Is it possible to provide a mechanism that make component can communicate with each other?

set_timeout is missing

The API currently wraps set_interval but does not provide a wrapper for set_timeout. Please consider adding this functionality as well.

Bug: Merge classes

I've just encountered a problem with classes and found this TODO in dom_types.rs:

// TODO:
/*
/// Tests that multiple class attributes are handled correctly
#[wasm_bindgen_test]
pub fn merge_classes() {
    let node = el_to_websys(a![
        class!["my_class1", "my_class2"],
        class!["my_class3"],
        attrs![
            At::Class => "my_class4 my_class5";
        ]
    ]);

    let mut expected = HashMap::new();
    expected.insert(
        "class".to_string(),
        "my_class1 my_class2 my_class3 my_class4 my_class5".to_string(),
    );
    assert_eq!(expected, get_node_attrs(&node));
}
*/

I'll try to fix it.

line macro interferes with assert macro

Consider (using seed v0.2.6):

#[macro_use]
extern crate seed;

fn main () {
    assert!(true);
}

cargo expand displays:

fn main() {
    if !true {

        {
            ::std::rt::begin_panic("assertion failed: true",
                                   &("iml-action-dropdown/src/lib.rs",
                                     {
                                         let mut el =
                                             El::empty_svg(seed::dom_types::Tag::Line);
                                         el
                                     }, 5u32))
        }
    };
}

I've tracked this down to the line! macro.

If we do not import the line! macro, the assert expands as expected.

allow custom attributes

Using an enum for attributes is a nice idea but does not work as long you're not able to user custom ones.
E.g. here is a log of my example app that has some SVG in it:

Can't find this attribute: transform
Can't find this attribute: x1
Can't find this attribute: y1                                                                                                                                                   
Can't find this attribute: x2
Can't find this attribute: y2
Can't find this attribute: x1
Can't find this attribute: y1
Can't find this attribute: x2
Can't find this attribute: y2
Can't find this attribute: fill
Can't find this attribute: stroke-width
Can't find this attribute: stroke
Can't find this attribute: d

And I don't want to create custom attributes in another way than usual ones.

Hooking into lifecycle events for cleanup

Description

Components have lifecycle hooks but do not provide a means to update the state of the app. This makes it difficult to cleanup any component actions. For example, consider a component that when mouseover occurs, will fetch data every ten seconds. When I leave the page and the component unmounts, I can catch the unmount event, but I have no way to send a message that would allow me to stop fetching.

I was able to handle this before the update to 0.3 by using state.update in the view function; but state is no longer passed in as an argument. At the moment, we are slowly integrating seed into an existing app, so we are not at the point where we are using routes (I think I could hook into there if I was). I think the easiest thing (from my point of view) would be to provide a way to send messages in lifecycle callbacks, similar to how events can send messages in their callback function. Is this something that can be done?

Regards,

Will

How to use setInterval?

First off, this is a pretty cool Rust web framework! ๐Ÿ‘


I was playing with the quickstart repo, and tried to add a setInterval example:

https://github.com/dashed/seed-quickstart/blob/70b3f2c76e5d205b06adb4734cfcc1a08f412c5c/src/lib.rs#L42-L57

fn view(state: seed::App<Msg, Model>, model: Model) -> El<Msg> {

    let callback = Closure::wrap(Box::new(move || {
        state.update(Msg::Increment);
    }) as Box<dyn Fn()>);

    div![
        did_mount(move |_| {
            let window = web_sys::window().unwrap();
            window
                .set_interval_with_callback_and_timeout_and_arguments_0(
                    // Note this method call, which uses `as_ref()` to get a `JsValue`
                    // from our `Closure` which is then converted to a `&Function`
                    // using the `JsCast::unchecked_ref` function.
                    callback.as_ref().unchecked_ref(),
                    1_000,
                )
                .unwrap();
        }),
        button![
            simple_ev("click", Msg::Increment),
            format!("Hello, World ร— {}", model.val)
        ]
    ]
}

But I'm getting this error:

screen shot 2018-12-31 at 12 39 55 pm

I'm pretty much new to WASM, and I'm unsure how to debug this ๐Ÿ˜ž; I'm hoping if there's some guidance to this.

view function returns only one element

Hi,
can I return more elements from root view?

I think that fn view(model: &Model) -> Vec<El<Msg>> would correspond more with quote from Seed documentation:

Fragments (<>...</> syntax in React and Yew) are components that represent multiple elements without a parent. They're useful to avoid unecessary divs, which clutter teh DOM, and breaks things like tables and CSS-grid.

Thanks!

Problem with El's text

Thank you David for your awesome work! I'm very new to Rust and Seed is the only frontend framework I find accessible.

But I discovered a problem. If I put multiple text nodes in an El, only the last one survives.

An example:
p![ "Today it's ", em!["not"], " gonna rain, this ", em!["is"], " so ", strong![ "good!" ] ]
becomes
<p> so <em>not</em><em>is</em><strong>good!</strong></p>

I suppose it's because the text in the El-struct is Option<String>. Maybe we need Vec<String>. But then it's still not clear where which text should go. Maybe it's best to have a special "Text"-Node that can go in the children-Vec.
For now we can work around this e.g. wrapping the text in <span> tags.
When I have a better understanding of the codebase I try to contribute a fix, but as I said, I'm very new to Rust.

Controlled components not possible

See yewstack/yew#233 (comment) for context.

Essentially, the desired behavior is: if an edit for a field (e.g. an input or text area) is 'rejected' (i.e. not applied to the model), the render should not update. In react this is known as 'controlled components'.

In seed you can test this by commenting out this line from the todomvc example and then trying to edit an item. Because the internal state isn't changing, arguably neither should the render. Unfortunately, it does.

React solves this by restoring state after a change event (described on the linked issue).

Session storage.

As far as I can tell, the current seed::storage module only exposes access to local storage.

Any reason session storage has not been exposed? Probably just a matter of it not having been implemented yet, but I figured I would just go ahead and pop an issue for it.

Happy to help code this up. Interface is pretty damn near identical.

Component lifecycle hooks

First, this framework looks very promising and the getting-started experience was very smooth thanks to the excellent documentation! I ported an app I started with yew to seed.

Now I would like to perform some DOM actions after a component has been inserted in the DOM, such as focus an input form element. Is this possible ?

Dynamic Routing

I want to share one thought of mine about dynamic routing: The most mind-blowing simple but fully functional routing solution (based on window.history) I have seen so far is one written in ReasonML:
Description
Code
Implementation is so much simpler than any of those react router implementations written in Javascript!

The secret sauce is just to split the URL (by /) into a list (ReasonML tail-recursive version of an array, or what in Rust is called Vec) of path segments, than the user of the api can recursively pattern match on that list to do his thing.

I am new to Rust, but it seems to me Rust pattern matching should also allow to do something like that.

Proposal: Debugging

My experience during debugging merging classes - I was mainly using:

  • command wasm-pack test --firefox --headless -- --lib merge_classes
  • alternative to console.log: log!(format!("Something: {:#?}", something))

I think that there are more user-friendly ways or we can create them. Add your ideas into this issue and we can resolve them in the future.

State of the project

seed is absolutely one of my favorite rust-wasm projects at the moment.
I really would like to build a real app with it but before investing too much time into it i'd like to know your thoughts about the current state and the future of seed.
Do you see seed more as an experiment or are you planning to move it into a production ready state?

Enums instead of &strs for Attrs/Events/Style

What do y'all think?

Eg instead of this:

input![ attrs!{"value" => model.what_we_count}, input_ev("input", Msg::ChangeWWC) ]

This:

input![ attrs!{At::Value => model.what_we_count}, input_ev(Ev::Input, Msg::ChangeWWC) ]

We can (and do in the case of tags and events) validate attrs/tags/events etc internally using Enums, but this might provide better real-time assessment, eg in IDEs. What do y'all think?

edit: Added this as an optional, eg the attrs! macro and event funcs accept either; leaning towards making it the default in guide/examples. Haven't done Style yet

todomvc does not destroy last item to be destroyed in list

Excellent work! The responsiveness of launching it directly through firefox was good.

the quick start works with one hiccup. no pkg dir. "mkdir pkg" fixes the issue.

I tried the todomvc example. added four items task1, task2, task3, task4, checked them off as completed, then clicked the "X" button at the far right of each item row. task1 deleted when I clicked task1, but task3 was deleted when I clicked task2. When I clicked clear completed, it did not destroy task2 either.

Proposal: Return Render implicitly

Suggested Changes

Return type:

fn update(msg: Msg, model: &mut Model) -> impl Updater<Msg> {
    ...
}

Why

Before:

fn update(msg: Msg, model: &mut Model) -> Update<Msg> {
    match msg {
        Msg::Increment => {
            model.count += 1;
            Render.into()
        }
        Msg::Decrement => { 
            model.count -= 1;
            Render.into()
        }
        Msg::Log(something) => {
            log!(something);
            Skip.into()
        }
    }    
}

After:

fn update(msg: Msg, model: &mut Model) -> impl Updater<Msg> {
    match msg {
        Msg::Increment => model.count += 1,
        Msg::Decrement => model.count -= 1,
        Msg::Log(something) => {
            log!(something);
            Skip
        }
    }
}
  • implicitly return Render
  • no need to write .into()
  • it would be easier to see "special" cases like Skip and ForceRender in source code
  • less boilerplate code
  • it would be a little bit easier for beginners to start working with Seed

Resolving Breaking Changes

No breaking changes.

Possible Implementation

Implement trait Updater for enum ShouldRender, struct Update and unit (). Unit will be de facto alias for Render.

Next Steps

I'll try to implement it after we agree on a scope of changes. Thanks for opinions and counter-arguments.

Conditional render on model update

If I understand properly, each time the update function changes the model, a render gets triggered.

This is a problem with my recent experiment to add animations based on a requestAnimationFrame loop to control spring-like animations. Whether the animations should run or not, depends on user interactions, e.g. if a button click triggers to open a modal then the animation fades or slides the modal in. I want to store the animation metadata (which changes during the lifetime of an animation) in a hashmap in the model. But updating that metadata triggers a 'render'. I don't want that, because the animations work directly on the DOM elements.

Here how some other frameworks handle conditional model update:

  • yew: the model is mutable and the update function returns a boolean, to indicate whether a render should be triggered
  • ReasonML: update returns a new model, like in seed, but if a field in the model is defined as mutable, then an update of only that field won't trigger a render. Would that work in seed ?

Another approach could be to let update return an Option<Model> and None would avoid a rerender.

[Update: The ReasonML approach will not work, Rust does not allow field mutability, I just learned]

How to update App state from JS

I try to run a seed app in a native wrapper, based on tether, for a desktop app. To get the native app interop with the seed webapp, I need somehow to update the seed app state from outside of the app loop. Is that possible ? Below are the relevant parts, and commented out, what I would like to do. Or is there a different way to achieve that kind of interoperability ?

...

#[wasm_bindgen]
pub fn render() {
  seed::run(Model::default(), update, view, "main", None);
}

#[wasm_bindgen]
pub fn my_exported_js_bridge(my_paylod: &str) -> () {
  // first we need to get the current model somehow
  // then: 

  // seed::eg_update_state(new_model_derived_from_my_paylod) ????
}

wasm-friendly Datetime utility crate

I'm considering adding a crate that wraps Chrono, but includes functionality it doesn't include on the wasm-target, like getting the current date/time/dt, via js_sys, and provides a cleaner API. Thoughts?

Caveats:

  • This is more suited to Gloo, but perhaps here is a better place to start, due to Gloo's formal update process, and since it's not-yet released. The API wrapper will be opinionated.

  • While getting the current date/time etc is common, it may not be worth wrapping a non-web crate just for this functionality, and api improvements are out of this project's scope. I'm proposing this anyway because a clean dt api that can get current info is common in web-programming, and this project takes a batteries-included approach: I don't want users to have to hunt around and use binder code.

@Pauan @fitzgen

Proposal: Mounting

Suggested Changes

1) New possible .mount(..) calls:

// A - HTMLElement argument
seed::App::build(Model::default(), update, view).mount(seed::body())

// B - &str argument
...mount("app")  // mount to element with `id` 'app'

// C - Element argument
....mount(seed::body().querySelector("section").unwrap().unwrap())

// D - Default mount point
seed::App::build(Model::default(), update, view) // not called => same as B

2)
Remove function .mount_el(..)

Why

  • One mount function per type (mount(&str), mount_el(Element), ..):
    • It's not very flexible for supporting more types in the future
    • It makes codebase/API a little bit more complicated
    • We have to write mount_el(seed::body().into()) if we want to pass HTMLElement as argument or implement another function like mount_html_el

Resolving Breaking Changes

Removing function .mount_el(..):

  • We can mark it deprecated and remove it in the future
  • It's missing in guide (https://seed-rs.org/guide/2), so no changes are necessary in docs

Possible Implementation

pub fn mount(mut self, mount_point: impl MountPoint) -> Self {
    ...
}

Next Steps

I'll implement it after we agree on a scope of changes. Thanks for opinions and counter-arguments.

Allow deserialization error handling in fetch

The Fetch module works great but it has a couple of expects where I would hope to have a Result that I can handle to say something went wrong with the request. Today you could do the string and Json deserialization manually and handle the errors but it would be nice if the built in functionality allowed for it.

How to get a exists element?

for example:
first question:
I want to change the window title like this:
window.title = "new title";
and how to do it via seed ?

second question:
i created a node: input ![..] and append a node: em! [..]
if user typed illegal letters, the node em![] change its text color to warn user: you are wrong..
how to do it via seed?

thank you very much. u r a nice person!

update_promises test fails sometimes

It seems to happen when another test panics. I added a panic to one of my tests in the merge-attrs
branch, and got the following:

  ---- seed::dom_types::tests::merge_different_attrs output ----
      error output:
          panicked at 'oops', src/dom_types.rs:1431:9
...

  ---- seed::vdom::tests::update_promises output ----
      error output:
          panicked at 'cannot modify the panic hook from a panicking thread', src/libstd/panicking.rs:99:9
  
      JS exception that was thrown:
          RuntimeError: unreachable executed
          std::panicking::rust_panic_with_hook::hfee0088857287f5e@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1392]:0x11d303
          std::panicking::begin_panic::hbb52db289a771dc8@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[6266]:0x1a4d04
          std::panicking::set_hook::hb4aa901e5df5948e@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[2968]:0x163abe
          <seed::vdom::App<Ms, Mdl>>::run::h6974a0b857137a67@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[224]:0x77b1a
          seed::vdom::tests::update_promises::hf39e5b7d86fcc21d@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[667]:0xd8458
          core::ops::function::FnOnce::call_once::h138edd608e53fa1e@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[6103]:0x1a306
          <futures::future::lazy::Lazy<F, R>>::get::hbdd74f92fdede09a@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[368]:0xa55f
          <futures::future::lazy::Lazy<F, R> as futures::future::Future>::poll::h679e659dd2c16847@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantia
          <wasm_bindgen_test::__rt::TestFuture<F> as futures::future::Future>::poll::{{closure}}::{{closure}}::hf6964e778384fd02@http://127.0.0.1:35831/wasm-bindgen-test_bg
          wasm_bindgen::convert::closures::invoke0_mut::hb742e1fd7f1ed33a@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1741]:0
          cbarg0@http://127.0.0.1:35831/wasm-bindgen-test:248:20
          window.__wbg_test_invoke@http://127.0.0.1:35831/:37:38
          __wbg_wbgtestinvoke_7cb78623c45caf3c@http://127.0.0.1:35831/wasm-bindgen-test:261:13
          wasm_bindgen_test::__rt::__wbg_test_invoke::hc29c5496854d23d3@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1030]:0x1
          <wasm_bindgen_test::__rt::TestFuture<F> as futures::future::Future>::poll::{{closure}}::hba1e17724b720a6e@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > Web
          <scoped_tls::ScopedKey<T>>::set::h2c0631d12c00fdc5@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1682]:0x12ecf6
          <wasm_bindgen_test::__rt::TestFuture<F> as futures::future::Future>::poll::h027bed2b453d6636@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.inst
          <alloc::boxed::Box<F> as futures::future::Future>::poll::h5cc15dea138dc8c3@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-funct
          <wasm_bindgen_test::__rt::ExecuteTests as futures::future::Future>::poll::hdca1c41a428258b9@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.insta
          <futures::future::map::Map<A, F> as futures::future::Future>::poll::hd38ea1b75a6600da@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate
          <futures::future::map_err::MapErr<A, F> as futures::future::Future>::poll::h9156cda28ef36195@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.inst
          <alloc::boxed::Box<F> as futures::future::Future>::poll::he7d6878934154090@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-funct
          <futures::task_impl::Spawn<T>>::poll_future_notify::{{closure}}::hfb739fefd52fbee2@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wa
          <futures::task_impl::Spawn<T>>::enter::{{closure}}::h101e3f9bd12f17cd@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[4
          futures::task_impl::std::set::hf47c0b1e18fe8ee8@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1701]:0x12fe5d
          <futures::task_impl::Spawn<T>>::enter::h851ac3033f34fc28@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1141]:0x10a1de
          <futures::task_impl::Spawn<T>>::poll_fn_notify::hae4a04219bf076cf@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[2198]
          <futures::task_impl::Spawn<T>>::poll_future_notify::h33e4757647d66c71@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[4
          wasm_bindgen_futures::_future_to_promise::Package::poll::h67e6d3dfb2d4e5ea@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-funct
          wasm_bindgen_futures::_future_to_promise::{{closure}}::h4171d10b7b9053ca@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-functio
          wasm_bindgen::convert::closures::invoke2_mut::h87ce774e1313d19e@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1052]:0
          cbarg0@http://127.0.0.1:35831/wasm-bindgen-test:1097:20
          __wbg_new_73c7b9beced23b50@http://127.0.0.1:35831/wasm-bindgen-test:1110:34
          js_sys::Promise::new::h5ba4d9a47d564f1d@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1666]:0x12de9b
          wasm_bindgen_futures::_future_to_promise::h8220e7422916ffdc@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[1278]:0x115
          wasm_bindgen_futures::future_to_promise::hb18f1446cf2ecb50@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[2196]:0x1483
          wasm_bindgen_test::__rt::Context::run::h1de5750c65d2f37d@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[215]:0x72f53
          wasmbindgentestcontext_run@http://127.0.0.1:35831/wasm-bindgen-test_bg line 8 > WebAssembly.instantiate:wasm-function[910]:0xf4567
          run@http://127.0.0.1:35831/wasm-bindgen-test:1355:27
          main@http://127.0.0.1:35831/run.js:35:19
  
  failures:
  
      seed::dom_types::tests::merge_different_attrs
      seed::vdom::tests::update_promises
  
  test result: FAILED. 15 passed; 2 failed; 0 ignored
  console.log div contained:
      panicked at 'oops', src/dom_types.rs:1431:9
      panicked at 'cannot modify the panic hook from a panicking thread', src/libstd/panicking.rs:99:9
  
  error: some tests failed
  error: test failed, to rerun pass '--lib'

seed-rs.org has invalid TLS cert.

Hopefully I didn't just stumble onto this project while you were updating the certs or doing some sort of maintenance. Either way, figured I would log an issue.

screen shot 2018-12-18 at 7 56 45 pm

Food for thought : WebRender backend in addition to DOM

Hi,

I like the direction the project is going with regards to the view templates, it looking like the Elm view DSL, rather than JSX, as well as the Elm inspired app model. As a matter of fact, I've been making such a DSL myself, but this implementation seems to be much more complete.

One idea that I considered interesting is in addition to working with DOM, is to wrap WebRender(https://github.com/servo/webrender) to make native widgets, sort of like this project (https://github.com/cztomsik/node-webrender), but the output would be entirely native binaries(and given WebRender is moving to use the native libraries as much a possible, the binaries should be very small as well).

I'm very much planning to do that, that being said, it might become a side project that goes nowhere.
So, wanted to ask if this project only plans to target the web via DOM.

Thanks

Intercept link clicks

A good priciple for SPAs is that navigation should work just like normal web pages. That means that links should be actual clickable links that take the user to a new URL. However, we don't actually want to force a page reload for internal links, but handle them within the app. Typically this is handled by adding a click handler to the link which redirects without reload, and this is possible with seed today. However at the moment seed does not intercept the default handling with event.preventDefault(), so in fact anchor tags with href = ... do still force a page reload.

This stackoverflow answer has a nice JS snippet that can be added at page load which will intercept link navigation. Suggestion: seed should call such a function at page load. The function should also check whether the link is internal or external - if external, it should be followed normally.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.