如何使用 Rust 和紫杉箱实现有状态应用程序导航?



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

github上的源文件:

我的目标是:

使用 Rust 和 Yew 板条箱实现有状态选项卡导航。

我已经设置了一个toggle_tab函数,该函数使用 STDweb 库通过tabcontent类、set attribute ("style", "display: none;")遍历现有的 HTML。然后我根据唯一的类名进行匹配(通过函数输入并从退出 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

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

应用外观

切换选项卡功能

网页代码

模型、状态、消息、筛选器

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

来自 Yew Gitter 的答案;

@robust系统(不查看提供的其他源代码,只查看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}
...

思潮

现在如何发出味精以及在哪里,我将在 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}
</>
}
}
}
}

相关内容

  • 没有找到相关文章

最新更新