[英]How to implement a Stateful App Navigation using Rust and the Yew Crate?
我正在尝试构建一个带有导航选项卡的应用程序,并尝试使用最佳方式为整个应用程序提供状态。 目前这些选项卡可以正常使用,但我正在努力实现一种方法,通过使用 Yew 前端单页应用程序框架的 Rust 状态来映射和恢复选项卡导航。 如何将这些选项卡及其各自的上下文部分链接到状态?
我的目标是:
使用 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
这非常重要,非常感谢您的任何指导。
我意识到这是一个相当抽象和冗长的问题,所以如果有任何进展,我会在这里更新。
@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.