繁体   English   中英

如何使用 Rust 和 Yew Crate 实现有状态的应用程序导航?

[英]How to implement a Stateful App Navigation using Rust and the Yew Crate?

我正在尝试构建一个带有导航选项卡的应用程序,并尝试使用最佳方式为整个应用程序提供状态。 目前这些选项卡可以正常使用,但我正在努力实现一种方法,通过使用 Yew 前端单页应用程序框架的 Rust 状态来映射和恢复选项卡导航。 如何将这些选项卡及其各自的上下文部分链接到状态?

github上的源文件:

我的目标是:

使用 Rust 和 Yew crate 实现有状态的选项卡导航。

我已经设置了一个 toggle_tab 函数,它使用 STDweb 库通过tabcontent类迭代现有的 HTML, set attribute ("style", "display: none;") 然后我根据唯一的类名进行匹配(通过函数输入并从退出 HTML 的onclick调用)。

Github 上Yew 示例中,我知道我可能需要将存储实现为状态并将状态实现为模型。 使用 Yew & Rust 实现应用导航的好方法是什么?

HTML 和切换功能:

impl Renderable<Model> for Model {
    fn view(&self) -> Html<Self> {
        html! {
    <>
        <nav class="tab grey lighten-2">
            <div class="nav-wrapper">
                // BUTTONS
                <ul class="row">
                    <div class="col s1" >
                    <button class="tablinks brand-logo welcome " onclick=|e| toggle_tab(e, ".welcome")>{"Welcome"}</button>
                    </div>
                    <div class="col s1 push-s1" >
                    <button class="tablinks brand-logo home active" onclick=|e| toggle_tab(e, ".home")>{"Home"}</button>
                    </div>
                </ul>
            </div>
        </nav>
        //SNIPPETS
        <div class="tabcontent white welcome content" >
                <Welcome/>
        </div>
        <div class="tabcontent white home content" style="display: block;">
//        <HomeTabs/>
        </div>
    </>
                        }
    }
}


fn toggle_tab(event: ClickEvent, tab_unique_class_name: &str) {
    use crate::stdweb::web::{IElement, IParentNode, INonElementParentNode};

    // Get all elements with class="tabcontent" and hide them
    for tab in document().query_selector_all(".tabcontent").unwrap() {
        let tab: Element = tab.try_into().unwrap();
        tab.set_attribute("style", "display: none;").unwrap();
    }

    // Get all elements with class="tablinks" and remove the class "active"
    for tab in document().query_selector_all(".tablinks").unwrap() {
        let tab: Element = tab.try_into().unwrap();
        tab.class_list().remove("active");
    }

    let matching_tabs = document().query_selector_all(tab_unique_class_name).unwrap();

    match tab_unique_class_name {
        ".welcome" => {
            for elem in matching_tabs.iter() {
                let elem: Element = elem.try_into().unwrap();
                elem.class_list().add("active");
                elem.set_attribute("style", "display: block");
            }
        }
        ".home" => {
            for elem in matching_tabs.iter() {
                let elem: Element = elem.try_into().unwrap();
                elem.class_list().add("active");
                elem.set_attribute("style", "display: block");
                document().get_element_by_id("dashboard").unwrap().set_attribute("style", "display: block;");
            }
        }
        ".campaign" => {
            for elem in matching_tabs.iter() {
                let elem: Element = elem.try_into().unwrap();
                elem.class_list().add("active");
                elem.set_attribute("style", "display: block");
            }
        }
        ".comming_soon" => {
            for elem in matching_tabs.iter() {
                let elem: Element = elem.try_into().unwrap();
                elem.class_list().add("active");
                elem.set_attribute("style", "display: block");
            }
        }
        _ => alert("Catchall WHoahw!"),
    }
}

源代码

如果看到更多代码有帮助,我在这里上传了当前的源代码https://github.com/robust-systems/Rust_MVC/blob/master/src/components/tabs_level_1.rs

这非常重要,非常感谢您的任何指导。

应用外观

切换标签功能

html代码

模型、状态、消息、过滤器

我意识到这是一个相当抽象和冗长的问题,所以如果有任何进展,我会在这里更新。

来自 Yew Gitter 的回答;

@robust-systems(不查看提供的其他源代码,仅查看 SO 帖子中可见的代码)我的建议是发出一个消息,其中包含一个枚举,该枚举表示您想要选择的标签页,而不是运行 toggle_tab . 您可以在更新中处理此消息并将枚举存储为模型的一部分,指示哪个处于活动状态。 从那里回过头来看,在模型中利用此状态的一种方法是创建您拼接在一起的小 html 片段。 例如:

let welcome_button = if let Tab::Welcome = self.active_tab {
    html!{
        <button class="tablinks brand-logo welcome active " onclick=self.link.callback(|_| Message::ChangeTab(Tab::Welcome))>{"Welcome"}</button>
    }
} else {
    html!{
        <button class="tablinks brand-logo welcome " onclick=self.link.callback(|_| Message::ChangeTab(Tab::Welcome))>{"Welcome"}</button>
    }
};

然后在你的大 html 中! 块,您插入`欢迎按钮,如

<ul class="row">
    <div class="col s1" >
    {welcome_button}
   ...

想法

现在如何发送 msg 以及发送到哪里,我将在 yew github 页面上编写自定义组件示例。 *将在应用程序完全有状态后编辑此答案。

更新

我终于让它完全有状态了! 这是任何碰巧发现它有用的人的结果代码。

我必须匹配一个 ChangeTab(Tab) 消息并将模型中的 active_tab 字段保存到本地存储,然后使用 if let 来恢复 active_tab。

use crate::button_welcome::WelcomeButton;
use crate::button_home::HomeButton;
use crate::button_comming_soon::CommingSoon;
use crate::button_debug_panel::DebugButton;

use serde_derive::{Deserialize, Serialize};

use yew::prelude::*;
use yew::services::storage::{StorageService, Area};
use yew::format::Json;
use crate::tab_navigation::Tab::Home;
use std::sync::atomic::Ordering::AcqRel;

const RESTORE_KEY: &'static str = "tab_navigation";

pub struct TabNavigation {
    active_tab: Tab,
    storage: StorageService,
}

pub enum Msg {
    ChangeTab(Tab),
}

#[derive(Serialize, Deserialize)]
pub enum Tab {
    Welcome,
    Home,
    CommingSoon,
    Debug,
}

impl Default for Tab {
    fn default() -> Self {
        Tab::Home
    }
}

impl Component for TabNavigation {
    type Message = Msg;
    type Properties = ();

    fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
        let storage = StorageService::new(Area::Local);
        let active_tab: Tab = {
            if let Json(Ok(active_tab)) = storage.restore(RESTORE_KEY) {
                active_tab
            } else {
                Tab::Home
            }
        };

        TabNavigation {
            storage: storage,
            active_tab: active_tab,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            // active tab enum
            Msg::ChangeTab(tab_to_activate) => {
                match tab_to_activate {
                    Tab::Welcome => {
                        self.active_tab = Tab::Welcome
                    },
                    Tab::Home => {
                        self.active_tab = Tab::Home
                    },
                    Tab::CommingSoon => {
                        self.active_tab = Tab::CommingSoon
                    },
                    Tab::Debug => {
                        self.active_tab = Tab::Debug
                    }
                }
            }
        }
        self.storage.store(RESTORE_KEY, Json(&self.active_tab));
        true
    }

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
//        if self.active_tab == Tab::Home
        true
    }
}

impl Renderable<TabNavigation> for TabNavigation {
    fn view(&self) -> Html<Self> {
        html! {
            // WELCOME
            let welcome_button = if let Tab::Welcome = self.active_tab {
                html! {
                    <WelcomeButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Welcome)) title="Welcome" class="active" />
                    // INSERT SECTION HERE
                }
            } else {
                html! {
                    <WelcomeButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Welcome)) title="Welcome" class="not-active" />
                }
            };
            // HOME
            let home_button = if let Tab::Home = self.active_tab {
                html! {
                    <HomeButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Home)) title="Home" class="active" />
                }
            } else {
                html! {
                    <HomeButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Home)) title="Home" class="not-active" />
                }
            };
            // COMMING SOON
            let comming_soon_button = if let Tab::CommingSoon = self.active_tab {
                html! {
                    <CommingSoon onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::CommingSoon)) title="Comming Soon" class="active" />
                }
            } else {
                html! {
                    <CommingSoon onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::CommingSoon)) title="Comming Soon" class="not-active" />
                }
            };
            // DEBUG
            let debug_button = if let Tab::Debug = self.active_tab {
                html! {
                    <DebugButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Debug)) title="Debug" class="active" />
                }
            } else {
                html! {
                    <DebugButton onsignal=self.link.callback(|_| Msg::ChangeTab(Tab::Debug)) title="Debug" class="not-active" />
                }
            };

            html! {
            <>
                {welcome_button}
                {home_button}
                {comming_soon_button}
                {debug_button}
            </>
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM