博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Asp.Net Core] Blazor Server Side 项目实践 - 切换页面时保留状态
阅读量:3963 次
发布时间:2019-05-24

本文共 4876 字,大约阅读时间需要 16 分钟。

前言:

这是 项目实践系列 , 算是中高级系列博文, 用于为项目开发过程中不好解决的问题提出解决方案的. 不属于入门级系列. 解释起来也比较跳跃, 只讲重点.

因为有网友的项目需求, 所以提前把这些解决方案做出来并分享.

 

问题:

Blazor自己是携带一个简单的路由功能的, 当切换Url的时候, 整个通过把RouteData传递给 App.razor 加载 MainLayout , 实现页面刷新的目的.

如果跳转到另外一个页面, 然后再跳回来的时候, 希望原来页面不刷新, 保留之前的状态 , 例如搜索条件, 那么怎么办?

 

解决过程:

结合视频, 图文观看效果最好 :  

1. 现在简单说说, 这种情况的源头在哪里.

2. App.razor 文件使用了 RouteView 来实现路由
3. routeData是包含页面类型, 以及页面参数的.
4. 然而默认的实现里, RouteView 是不带状态的
5. MainLayout虽然得到了内容的 RenderFragment ,
6. 然而这个 RenderFragment是由RouteView直接绑定到routeData上面去.
7. 所以MainLayout无法得到不同的RenderFragment来显示不同的内容.
8. 要解决这个问题, 首先第一步就是改造 RouteView

改造 RouteView

using System;using System.Collections.Generic;using Microsoft.AspNetCore.Components.Rendering;using System.Reflection;namespace Microsoft.AspNetCore.Components   //use this namepace so copy/paste this code easier{    public class KeepPageStateRouteView : RouteView    {        protected override void Render(RenderTreeBuilder builder)        {            var layoutType = RouteData.PageType.GetCustomAttribute
()?.LayoutType ?? DefaultLayout; builder.OpenComponent
(0); builder.AddAttribute(1, "Layout", layoutType); builder.AddAttribute(2, "ChildContent", (RenderFragment)CreateBody()); builder.CloseComponent(); } RenderFragment CreateBody() { var pagetype = RouteData.PageType; var routeValues = RouteData.RouteValues; void RenderForLastValue(RenderTreeBuilder builder) { //dont reference RouteData again builder.OpenComponent(0, pagetype); foreach (KeyValuePair
routeValue in routeValues) { builder.AddAttribute(1, routeValue.Key, routeValue.Value); } builder.CloseComponent(); } return RenderForLastValue; } }}

 

Blazor自带的RouteView是一个控件. 它每次呈现, 都使用 RouteData 属性, 所以它每次生成的 RenderFragment 都是跟着最后的 RouteData 走, 保存来没用.

改造后的 KeepPageStateRouteView , 使用 CreateBody() 方法, 创建出绑定 pagetype 和 routevalue 的 RenderFragement , 为 MainLayout 打下基础

 

改造 MainLayout 

@inherits LayoutComponentBase@inject NavigationManager navmgr@code{    TimeSpan GetUrlMaxLifeSpan(string url)    {        if (url.Contains("/fetchdata")) // Let /fetachdata always refresh            return TimeSpan.Zero;        if (url.Contains("/counter"))   // Let /counter expires in 10 seconds            return TimeSpan.FromSeconds(10);        return TimeSpan.FromSeconds(-1);    //other pages never expires    }    class PageItem    {        public string Url;        public RenderFragment PageBody;        public DateTime StartTime = DateTime.Now;        public DateTime ActiveTime = DateTime.Now;        public TimeSpan MaxLifeSpan;    }    Dictionary
bodymap = new Dictionary
(); int mainRenderCount = 0;}
@{ bool currurlrendered = false; string currenturl = navmgr.Uri; PageItem curritem; if (bodymap.TryGetValue(currenturl, out curritem)) { curritem.ActiveTime = DateTime.Now; } else { curritem = new PageItem { Url = currenturl, PageBody = Body }; curritem.MaxLifeSpan = GetUrlMaxLifeSpan(currenturl); if (curritem.MaxLifeSpan != TimeSpan.Zero) { bodymap[navmgr.Uri] = curritem; } } mainRenderCount++; }
#@mainRenderCount , CurrentUrl : @currenturl . PageCount : @bodymap.Count ,
@foreach (PageItem eachitem in bodymap.Values.ToArray()) { string pageurl = eachitem.Url; RenderFragment pagebody = eachitem.PageBody; string divstyle = "display:none"; if (pageurl == currenturl) { divstyle = ""; currurlrendered = true; } else if (eachitem.MaxLifeSpan.TotalSeconds > 0 && DateTime.Now - eachitem.ActiveTime > eachitem.MaxLifeSpan) { bodymap.Remove(eachitem.Url); continue; }
@pagebody
} @if (!currurlrendered) {
@Body
}

 

MainLayout 里, 最关键的是 Dictionary<string, PageItem> bodymap = new Dictionary<string, PageItem>();

这个字典, Key 是 Url , 而 PageItem 则储存了这个 Url 的多个信息.

范例使用了 TimeSpan GetUrlMaxLifeSpan(string url) 函数来制定页面的生存时间规则.

如果页面的生存时间是 0 , 表示不加进 bodymap , 每次都要全部刷新.

生存时间不为0 , 就储存到 bodymap 里面去. 然后在

@foreach (PageItem eachitem in bodymap.Values.ToArray())

循环过程中, 把每一个页面 Render 出来.

当前页面, 就显示, 不是当前页面, 则 display:none

没错. 在 Blazor 的 Render 体系里 , 只有输出了, 才有生命. 不输出, 就会被系统释放.

所以, 所有要让它活着的 Page , 都得输出. 哪怕用display:none隐藏它.

看看视频效果吧.

 

最后:

github :  

 

下一个版本: 支持多Tabs

 

暂时没时间做视频写博客.  后面补上. 

 

视频杂音的确多, 求推荐一个麦克风..

 

转载地址:http://zcuki.baihongyu.com/

你可能感兴趣的文章
Flutter UI基础 - Widgets 之 InkWell 和 Ink
查看>>
Spring - sentinel和hystrix比较
查看>>
MySQL - 索引之B+树
查看>>
Spring - Dubbo的底层实现原理和机制
查看>>
Flutter Dio引入和简单的Get/Post请求
查看>>
Flutter Dart 和 Flutter json转实体类(插件自动生成)
查看>>
Flutter 路由跳转fluro
查看>>
Flutter 日期插件date_format 中文 国际化 及flutter_cupertino_date_picker
查看>>
Flutter 插件笔记 | 屏幕适配 flutter_screenutil
查看>>
Flutter UI基础 - 侧拉抽屉菜单
查看>>
Flutter UI基础 - AppBar中标题文字如何居中
查看>>
Flutter UI基础 - Drawer 抽屉视图与自定义header
查看>>
Flutter UI基础 - 点击展开和关闭
查看>>
Flutter UI基础 - GridView
查看>>
Flutter UI - 打造一个圆形滑块(Slider)
查看>>
Flutter UI基础 - 分割线效果实现
查看>>
Flutter UI基础 - DecoratedBox组件
查看>>
Flutter UI基础 - 使用InkWell给任意Widget添加点击事件
查看>>
OC WKWebView的使用
查看>>
Flutter UI基础 - Image.asset 图片铺满布局
查看>>