mc1098 ed2e1ea00e
Add testing for website code blocks (#2014)
* Add doc-test to test website code snippets

Heavily inspired by tokio-rs/website repo.

* Fix code snippets to pass doc tests

Some code snippets are explicitly ignored and some are not run
to avoid having to include dependencies for one liners.

* Add website code snippet tests to CI

* Fix CI

* Remove doc-test from workspace

* Exclude doc-test from workspace

* Refactor code snippets and tests

Code snippets can import types from doc_test crate i.e.:
```rust
use doc_test::agents::EventBus;
```
This allows for moving some boilerplate away from the example and still
checks that the code compiles correctly.

Also some slight changes to some of the examples and the information
about `ComponentLink` which is deprecated.

* Move doc-test to packages

* Rename doc-test crate to website-test

The new name makes it more clear the purpose of this crate.

* fix ci
2021-08-28 13:17:28 +02:00

4.0 KiB

title description
Properties 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

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 PartialEq.

Field attributes

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. :::

#[prop_or_default]

Initialize the prop value with the default value of the field's type using the Default trait.

#[prop_or(value)]

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.

PartialEq

Properties require PartialEq to be implemented. This is so that they can be compared by Yew to call the changed method only when they change.

Memory/speed overhead of using Properties

Internally properties are reference counted. This means that only a pointer is passed down the component tree for props. It saves us from the cost of having to clone the entire props, which might be expensive.

Example

use std::rc::Rc;
use yew::Properties;

#[derive(Clone, PartialEq)]
pub enum LinkColor {
    Blue,
    Red,
    Green,
    Black,
    Purple,
}

fn create_default_link_color() -> LinkColor {
    LinkColor::Blue
}

#[derive(Properties, PartialEq)]
pub struct LinkProps {
    /// The link must have a target.
    href: String,
    text: String,
    /// 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]
    size: Option<u32>,
    /// When the view function doesn't specify active, it defaults to true.
    #[prop_or(true)]
    active: bool,
}

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).

use std::rc::Rc;
use yew::{props, Properties};

#[derive(Clone, PartialEq)]
pub enum LinkColor {
    Blue,
    Red,
    Green,
    Black,
    Purple,
}

fn create_default_link_color() -> LinkColor {
    LinkColor::Blue
}

#[derive(Properties, PartialEq)]
pub struct LinkProps {
    /// The link must have a target.
    href: String,
    text: Rc<String>,
    /// 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]
    size: Option<u32>,
    /// When the view function doesn't specify active, it defaults to true.
    #[prop_or(true)]
    active: bool,
}

impl LinkProps {
    pub fn new_link_with_size(href: String, text: String, size: u32) -> Self {
        // highlight-start
        props! {LinkProps {
            href,
            text: Rc::from(text),
            size,
        }}
        // highlight-end
    }
}