diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index f765ce6a7b5..54abca63e7d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -82,7 +82,8 @@ jobs: doc_tests: name: Documentation Tests - runs-on: ubuntu-latest + # Using 20.04 because 18.04 (latest) only has aspell 0.60.7 and we need 0.60.8 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -103,7 +104,9 @@ jobs: cargo-${{ runner.os }}- - name: Check spelling - run: bash ci/spellcheck.sh list + run: | + sudo apt-get install aspell + ci/spellcheck.sh list - name: Run doctest uses: actions-rs/cargo@v1 diff --git a/ci/dictionary.txt b/ci/dictionary.txt index 636997f8ed7..1dd653ef3d4 100644 --- a/ci/dictionary.txt +++ b/ci/dictionary.txt @@ -1,20 +1,11 @@ -personal_ws-1.1 en 88 utf-8 -Angular's -Config -Deref -Github -Json -Lifecycle -React's -Todo -VecDeque -Webpack +personal_ws-1.1 en 0 utf-8 alloc +ALLOC allocator +Angular's asmjs -backends -barebones binaryen +Binaryen bindgen bool boolean @@ -23,47 +14,58 @@ charset codebase codegen composable +Config declaratively -doctype +defs +DerefMut +DOCTYPE +DOCUSAURUS emscripten +Emscripten enum enums +Github href html +Html +IHtmlElement impl init +INIT interop -interoperability -interoperable -lang -libs lifecycle +Lifecycle +linecap +linejoin memoized -metaprogramming -minimize -miniserve -mkdir natively onclick proc +React's +README rlib roadmap +Roadmap rollup -rustc -rustfmt -rustwasm -stacktraces +rustup stdweb struct structs tbody +textarea thead +TODO toml +Unselected usize -vdom -vtag +VecDeque +Vuetify wasm +Wasm webpack +Webpack +WeeAlloc workspaces -vuetify +xmlns yewtil +Yewtil diff --git a/ci/spellcheck.sh b/ci/spellcheck.sh index 2d07fa03c38..5a9e89bf7fc 100755 --- a/ci/spellcheck.sh +++ b/ci/spellcheck.sh @@ -60,8 +60,8 @@ if [[ ! -f "$dict_filename" ]]; then echo "Scanning files to generate dictionary file '$dict_filename'." echo "Please check that it doesn't contain any misspellings." - echo "personal_ws-1.1 en 0 utf-8" > "$dict_filename" - cat "${markdown_sources[@]}" | aspell --ignore 3 list | sort -u >> "$dict_filename" + echo "personal_ws-1.1 en 0 utf-8" >"$dict_filename" + cat "${markdown_sources[@]}" | aspell --ignore 3 --camel-case list | sort -u >>"$dict_filename" elif [[ "$mode" == "list" ]]; then # List (default) mode: scan all files, report errors. declare -i retval=0 @@ -74,7 +74,7 @@ elif [[ "$mode" == "list" ]]; then fi for fname in "${markdown_sources[@]}"; do - command=$(aspell --ignore 3 --camel-case --personal="$dict_path" "$mode" < "$fname") + command=$(aspell --ignore 3 --camel-case --personal="$dict_path" "$mode" <"$fname") if [[ -n "$command" ]]; then for error in $command; do # FIXME: find more correct way to get line number @@ -96,6 +96,6 @@ elif [[ "$mode" == "check" ]]; then fi for fname in "${markdown_sources[@]}"; do - aspell --ignore 3 --dont-backup --personal="$dict_path" "$mode" "$fname" + aspell --ignore 3 --camel-case --dont-backup --personal="$dict_path" "$mode" "$fname" done fi diff --git a/docs/concepts/components.md b/docs/concepts/components.md index 90140106a34..0bd32043e44 100644 --- a/docs/concepts/components.md +++ b/docs/concepts/components.md @@ -2,6 +2,7 @@ title: Introduction description: Components and their lifecycle hooks --- + ## What are Components? Components are the building blocks of Yew. They manage their own state and can render themselves to the DOM. Components are created by implementing the `Component` trait for a type. The `Component` @@ -20,7 +21,7 @@ in the lifecycle of a component. When a component is created, it receives properties from its parent component as well as a `ComponentLink`. The properties can be used to initialize the component's state and the "link" can be used to register callbacks or send messages to the component. -It is common to store the props (data which can be passed from parent to child components) and the +It is common to store the props (data which can be passed from parent to child components) and the `ComponentLink` in your component struct, like so: ```rust @@ -43,10 +44,10 @@ impl Component for MyComponent { ### View -The `view` method allows you to describe how a component should be rendered to the DOM. Writing -HTML-like code using Rust functions can become quite messy, so Yew provides a macro called `html!` -for declaring HTML and SVG nodes (as well as attaching attributes and event listeners to them) and a -convenient way to render child components. The macro is somewhat similar to React's JSX (the +The `view` method allows you to describe how a component should be rendered to the DOM. Writing +HTML-like code using Rust functions can become quite messy, so Yew provides a macro called `html!` +for declaring HTML and SVG nodes (as well as attaching attributes and event listeners to them) and a +convenient way to render child components. The macro is somewhat similar to React's JSX (the differences in programming language aside). ```rust @@ -66,9 +67,9 @@ For usage details, check out [the `html!` guide](html.md). ### Rendered -The `rendered` component lifecycle method is called once `view` has been called and Yew has rendered +The `rendered` component lifecycle method is called once `view` has been called and Yew has rendered the results to the DOM, but before the browser refreshes the page. This method is useful when you -want to perform actions that can only be completed after the component has rendered elements. There +want to perform actions that can only be completed after the component has rendered elements. There is also a parameter called `first_render` which can be used to determine whether this function is being called on the first render, or instead a subsequent one. @@ -107,8 +108,8 @@ Note that this lifecycle method does not require an implementation and will do n ### Update Communication with components happens primarily through messages which are handled by the -`update` lifecycle method. This allows the component to update itself -based on what the message was, and determine if it needs to re-render itself. Messages can be sent +`update` lifecycle method. This allows the component to update itself +based on what the message was, and determine if it needs to re-render itself. Messages can be sent by event listeners, child components, Agents, Services, or Futures. Here's an example of what an implementation of `update` could look like: @@ -140,8 +141,8 @@ impl Component for MyComponent { ### Change -Components may be re-rendered by their parents. When this happens, they could receive new properties -and need to re-render. This design facilitates parent to child component communication by just +Components may be re-rendered by their parents. When this happens, they could receive new properties +and need to re-render. This design facilitates parent to child component communication by just changing the values of a property. A typical implementation would look something like: @@ -163,7 +164,7 @@ impl Component for MyComponent { ### Destroy -After Components are unmounted from the DOM, Yew calls the `destroy` lifecycle method; this is +After Components are unmounted from the DOM, Yew calls the `destroy` lifecycle method; this is necessary if you need to undertake operations to clean up after earlier actions of a component before it is destroyed. This method is optional and does nothing by default. @@ -180,15 +181,15 @@ impl Component for MyComponent { } ``` -The `Message` type is used to send messages to a component after an event has taken place; for -example you might want to undertake some action when a user clicks a button or scrolls down the +The `Message` type is used to send messages to a component after an event has taken place; for +example you might want to undertake some action when a user clicks a button or scrolls down the page. Because components tend to have to respond to more than one event, the `Message` type will normally be an enum, where each variant is an event to be handled. -When organising your codebase, it is sensible to include the definition of the `Message` type in the -same module in which your component is defined. You may find it helpful to adopt a consistent naming -convention for message types. One option (though not the only one) is to name the types -`ComponentNameMsg`, e.g. if your component was called `Homepage` then you might call the type +When organizing your codebase, it is sensible to include the definition of the `Message` type in the +same module in which your component is defined. You may find it helpful to adopt a consistent naming +convention for message types. One option (though not the only one) is to name the types +`ComponentNameMsg`, e.g. if your component was called `Homepage` then you might call the type `HomepageMsg`. ```rust diff --git a/docs/concepts/components/properties.md b/docs/concepts/components/properties.md index 6b9b0e8a4a2..1e6cb03a5b8 100644 --- a/docs/concepts/components/properties.md +++ b/docs/concepts/components/properties.md @@ -2,23 +2,50 @@ title: Properties description: Parent to child communication --- + Properties enable child and parent components to communicate with each other. +Every component has an associated properties type which describes what is passed down from the parent. +In theory this can be any type that implements the `Properties` trait, but in practice there's no +reason for it to be anything but a struct where each field represents a property. ## Derive macro -Don't try to implement `Properties` yourself, derive it by using `#[derive(Properties)]` instead. +Instead of implementing the `Properties` trait yourself, you should use `#[derive(Properties)]` to +automatically generate the implementation instead. +Types for which you derive `Properties` must also implement `Clone`. + +### Field attributes -:::note -Types for which you derive `Properties` must also implement `Clone`. This can be done by either using `#[derive(Properties, Clone)` or manually implementing `Clone` for your type. +When deriving `Properties`, all fields are required by default. +The following attributes allow you to give your props initial values which will be used unless they're set to another value. + +:::tip +Attributes aren't visible in Rustdoc generated documentation. +The docstrings of your properties should mention whether a prop is optional and if it has a special default value. ::: -### Required attributes +#### `#[prop_or_default]` + +Initialize the prop value with the default value of the field's type using the `Default` trait. -The fields within a struct that derives `Properties` are required by default. When a field is missing and the component is created in the `html!` macro, a compiler error is returned. For fields with optional properties, use the `#[prop_or_default]` attribute to use the default value for that type when the prop is not specified. To specify a value, use the `#[prop_or(value)]` attribute where value is the default value for the property or alternatively use `#[prop_or_else(function)]` where `function` returns the default value. For example, to default a boolean value as `true`, use the attribute `#[prop_or(true)]`. It is common for optional properties to use the `Option` enum which has the default value `None`. +#### `#[prop_or(value)]` -### PartialEq +Use `value` to initialize the prop value. `value` can be any expression that returns the field's type. +For example, to default a boolean prop to `true`, use the attribute `#[prop_or(true)]`. + +#### `#[prop_or_else(function)]` + +Call `function` to initialize the prop value. `function` should have the signature `FnMut() -> T` where `T` is the field type. + +:::warning +The function is currently called even if the prop is explicitly set. If your function is performance intensive, consider using `Option` where `None` values are initialized in the `create` method. +See [#1623](https://github.com/yewstack/yew/issues/1623) +::: -It is likely to make sense to derive `PartialEq` on your props if you can do this. Using `PartialEq` makes it much easier to avoid unnecessary rerendering \(this is explained in the **Optimizations & Best Practices** section\). +## PartialEq + +It makes sense to derive `PartialEq` on your props if you can do so. +Using `PartialEq` makes it much easier to avoid unnecessary rendering \(this is explained in the **Optimizations & Best Practices** section\). ## Memory/speed overhead of using Properties @@ -45,11 +72,8 @@ pub enum LinkColor { Purple, } -impl Default for LinkColor { - fn default() -> Self { - // The link color will be blue unless otherwise specified. - LinkColor::Blue - } +fn create_default_link_color() -> LinkColor { + LinkColor::Blue } #[derive(Properties, Clone, PartialEq)] @@ -58,9 +82,9 @@ pub struct LinkProps { href: String, /// If the link text is huge, this will make copying the string much cheaper. /// This isn't usually recommended unless performance is known to be a problem. - text: Rc, - /// Color of the link. - #[prop_or_default] + text: Rc, + /// Color of the link. Defaults to `Blue`. + #[prop_or_else(create_default_link_color)] color: LinkColor, /// The view function will not specify a size if this is None. #[prop_or_default] @@ -71,3 +95,23 @@ pub struct LinkProps { } ``` +## Props macro + +The `yew::props!` macro allows you to build properties the same way the `html!` macro does it. + +The macro uses the same syntax as a struct expression except that you can't use attributes or a base expression (`Foo { ..base }`). +The type path can either point to the props directly (`path::to::Props`) or the associated properties of a component (`MyComp::Properties`). + +```rust +let props = yew::props!(LinkProps { + href: "/", + text: Rc::from("imagine this text being really long"), + size: 64, +}); + +// build the associated properties of a component +let props = yew::props!(Model::Properties { + href: "/book", + text: Rc::from("my bestselling novel"), +}); +``` diff --git a/docs/concepts/html.md b/docs/concepts/html.md index 914167f02e8..f60a730f812 100644 --- a/docs/concepts/html.md +++ b/docs/concepts/html.md @@ -4,21 +4,126 @@ sidebar_label: Introduction description: The procedural macro for generating HTML and SVG --- -The `html!` macro allows you to write HTML and SVG code declaratively. It is similar to JSX +The `html!` macro allows you to write HTML and SVG code declaratively. It is similar to JSX \(an extension to JavaScript which allows you to write HTML-like code inside of JavaScript\). **Important notes** -1. The `html!` macro only accepts one root html node \(you can counteract this by -[using fragments or iterators](html/lists.md)\) +1. The `html!` macro only accepts one root html node \(you can counteract this by + [using fragments or iterators](html/lists.md)\) 2. An empty `html! {}` invocation is valid and will not render anything 3. Literals must always be quoted and wrapped in braces: `html! { "Hello, World" }` :::note -The `html!` macro can reach easily the default recursion limit of the compiler. It is advised to -bump its value if you encounter compilation errors. Use an attribute like -`#![recursion_limit="1024"]` in the crate root \(i.e. either `lib.rs` or `main.rs`\) to overcome the -problem. +The `html!` macro can reach the default recursion limit of the compiler. If you encounter compilation errors, add an attribute like `#![recursion_limit="1024"]` in the crate root to overcome the problem. +::: -See the [official documentation](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute) and [this Stack Overflow question](https://stackoverflow.com/questions/27454761/what-is-a-crate-attribute-and-where-do-i-add-it) for details. -::: \ No newline at end of file +## Tag Structure + +Tags are based on HTML tags. Components, Elements, and Lists are all based on this tag syntax. + +Tags must either self-close `<... />` or have a corresponding end tag for each start tag. + + + + +```rust +html! { +
+} +``` + + + +```rust +html! { +
// <- MISSING CLOSE TAG +} +``` + + + +```rust +html! { + +} +``` + + + +```rust +html! { + // <- MISSING SELF-CLOSE +} +``` + + + +:::tip +For convenience, elements which _usually_ require a closing tag are **allowed** to self-close. For example, writing `html! {
}` is valid. +::: + +## Children + +Create complex nested HTML and SVG layouts with ease: + + + + +```rust +html! { +
+
+
+ + + + +