Component API

This document introduces the core API of the component system, including component import, property declaration, event management, etc., to help developers build and manage custom components more efficiently.

import!

The import! macro is used to import custom components so that they can be used in the current scope.

Usage

1import! {
2    crate::moudle::to::component;
3}

Example

Import Rust components into your structure using import!:

1<template>
2    <component name="MyView2">
3        <Header></Header>
4        <MyView1></MyView1>
5    </component>
6</template>
7
8<script>
9import! {
10    crate::views::my_view1::*;
11    crate::components::header::*;
12}
13</script>

#[component]

For custom components, we need to use the #[component] macro to declare the component's properties, but not all types are allowed. Types that can be used in properties need to implement the Default trait, and custom structs and enums need to be annotated with #[prop].

1<script>
2#[component]
3pub struct MyView{
4    name: String
5}
6</script>

#[prop]

The #[prop] macro is used to define the properties (Props) of a component. We need to use the #[prop] macro to declare properties so that they can be assigned when the component is instantiated.

Limitations

Not all types can be used as prop. Only types that implement the Default trait can be used for property declaration.

Example

1<script>
2#[component]
3pub struct MyView{
4    user: User,
5    auth: Auth
6}
7
8#[prop]
9#[derive(Debug, Clone)]
10pub struct User{
11    name: String
12}
13
14impl Default for User{
15    fn default() -> Self{
16        Self{ name: "John" }
17    }
18}
19
20#[prop]
21#[derive(Default, Clone, Copy, Debug)]
22pub enum Auth{
23    #[default]
24    Custom,
25    User,
26    Remote,
27    Other
28}
29</script>

get|set method

All properties declared in components and bound in templates will automatically generate corresponding get and set methods. Please use the get|set methods to access and modify property values, otherwise the two-way binding will fail.

For example, the above MyView structure will automatically generate the following methods:

  • fn get_user(&self) -> User;
  • fn set_user(&mut self, value: User) -> ();
  • fn get_auth(&self) -> Auth;
  • fn set_auth(&self, value: Auth) -> ();

Default trait

The Default trait is used to initialize the property values ​​of a component instance.

Example

1<script>
2#[component]
3pub struct MyView {
4    name: String,
5}
6
7impl Default for MyView{
8    fn default() -> Self{
9        Self{
10            name: "John".to_string(),
11        }
12    }
13}
14</script>

In subsequent code, you can implement component methods and callback functions just like implementing impl, for example:

1impl MyView{
2    fn click_btn(&mut self) -> (){
3        let name = self.get_name();
4        self.set_name("Alice".to_string());
5    }
6}

#[event]

Custom components do not trigger any events by default. To use the event system, you need to use the #[event] macro to declare events.

Addention

  • Events must be marked with #[event].
  • Event types must derive from the Debug and Clone traits.

Example

1#[event]
2#[derive(Debug, Clone)]
3pub enum MyViewEvent {
4    Clicked,
5    Changed(String),
6}

In the above example, we define the MyViewEvent event type, where:

  1. Clicked: a click event without parameters.
  2. Changed(String): a change event with a String parameter.

c_ref!

The c_ref! macro is used to obtain a component reference based on the component id, making it easier to directly manipulate the reference of a component instance.

Example

1<template>
2    <component name="MyView">
3        <label id="my_label" text="'Hello'"></label>
4        <button id="my_btn" @clicked="get_label_text()">
5            <label as_prop="slot" text="'get my label text'"></label>
6        </button>
7    </component>
8</template>
9
10<script>
11#[component]
12struct MyView{}
13
14impl MyView{
15    fn get_label_text(&mut self) {
16        let label = c_ref!(my_label);
17        let label_text = label.get_text();
18        println!("label text is: {}", label_text);
19    }
20}
21</script>

active!

The active! macro is used to trigger an event defined inside a component and pass it to an external component for callback processing.

Example

MyView Component

1#[event]
2#[derive(Debug, Clone)]
3pub enum MyViewEvent {
4    Clicked,
5    Changed(String),
6}
7
8impl MyView{
9    fn click_view(&self) {
10        let _ = active!(MyViewEvent::Clicked);
11    }
12
13    fn change_view(&self) {
14        active!(MyViewEvent::Changed, "changed!".to_string());
15    }
16}

FatherView Component

The FatherView component listens to the events of MyView:

1<template>
2    <component name="FatherView">
3        <MyView 
4            @clicked="my_view_clicked()" 
5            @changed="my_view_changed()">
6        </MyView>
7    </component>
8</template>
9
10<script>
11#[component]
12struct FatherView{}
13
14impl FatherView{
15    fn my_view_clicked(&self) {
16        dbg!("my_view_clicked!");
17    }
18
19    fn my_view_changed(&self, param: impl EventParam) {
20        dbg!(param);
21    }
22}
23</script>

#[computed]

Computed properties are values ​​dynamically calculated based on existing properties. In GenUI, computed properties do not store state, but are automatically updated based on changes in their dependencies.

FeaturesComputedTwo-way Binding
Store state?❌ No✅ Yes
Mutable?✅ Update when dependencies change✅ Yes
Manually editable?✅ But cannot use set_xxx method✅ Modify via set_xxx
Dependent data✅ Requires dependencies✅ Component state

Usage

  1. #[computed] needs to be declared in the method
  2. #[computed] depends on the fields declared in the component, which can be multiple
  3. The return value of the calculated property needs to be consistent with the component binding
  4. The calculated property is equivalent to handing over the value processing to the developer, so set_xxx() cannot be used in the calculated property method to change the component field. Instead, a more flexible assignment change is used
1impl Hello{
2    #[computed([$(arg)*])]
3    fn $computed_fn($[&self|&mut self]?) -> $return_value_type{
4        // ...
5    }   
6}
  • $(arg)*: Depends on the parameter list, indicating that there can be one or more
  • $computed_fn: Calculated attribute processing function
  • $[&self|&mut self]?: Indicates that it can be &self or &mut self, which is up to you to decide
  • $return_value_type: Return value type

Example

The way to write a calculated property is to use a method to declare it when binding the property:

1<template>
2    <component name="Hello" class="hello_view" spacing="12.0">
3        <label :text="fmt_count()"></label>
4    </component>
5</template>
6
7<script>
8#[component]
9pub struct Hello{
10    count: u32,
11}
12
13impl Hello{
14    #[computed([count])]
15    fn fmt_count(&self) -> String{
16        format!("count: {}", self.count)
17    }   
18}
19</script>

Used to jump from a sub-page of routing management to other sub-pages.

Usage

1nav_to!($page_id);
  • $page_id: id of the page
router.toml
1[bar_pages]
2login = { path = "crate::views::login::*", component = "Login" }

The login key configured in router.toml is the page id

Example

1impl Hello{
2    pub fn to_register(&mut self) {
3        nav_to!(register);
4    }
5}

Return the route to the previous page by pushing the stack, if any.

Usage

1nav_back!();

Example

impl Hello{ pub fn back(&mut self) { nav_back!(); } }