C# .NETCORE3.1 系列教程(三) 控制器中控制页面弹出提示框功能实现(ViewComponent实现)

实现效果

视图页展示

 

Controller的Action调用方法

了解ViewComponent
ViewComponent,简称视图组件,你可以在任何可重复使用的渲染逻辑的部分视图中用视图组件来替换,那么就能够实现在一个View中嵌入一些相同或类似的内容(相当于能在他的父视图的对应位置插入相应的Html代码)。当然,.net也有提供一个partial views可以实现,或者使用ChildConrtoller,但是,partial views并不能另外写一个控制器进行数据的特殊处理,而ChildConrtoller的开销则比较大,效率更低,因为需要走完整个Controller的过程。ViewComponent提供了一个独立的控制器,可以让你在服务端中对数据进行查询、计算和控制。

因为网上没有比较直观的定义,所以这个定义是我的个人理解。

比如一个网页,每一页的顶部和导航栏可能是相同或类似的,每个页面去写比较麻烦,而且如果需要修改,你需要在非常多页里面进行修改,这样做的方式并不友好,所以,此时你就考虑写一个视图组件,修改时只要修改这个组件,其他页在相应位置嵌入这个组件即可。

我们的这个弹窗也可以看作一个组件,在每个页面底部都嵌入这个组件,当发现传入的msg有内容时,就可以弹窗一个弹窗(如用js的alert函数,或者如果你有引入其他前端框架你可以写成其他的方式),否则这个组件不会做任何操作。

创建ViewComponent控制器
我们需要先在项目中创建一个名为“ViewComponents”的目录,按照约定,所有自定义视图组件的控制器类默认情况下都存放在这个目录下。

我们在这个目录下创建一个名为“MessageBox”的视图组件控制器,我们创建一个类,名为MessageBoxViewComponent(按约定,视图组件的控制器类都组件名称+“ViewComponent”命名)

MessageBoxViewComponent 继承Microsoft.AspNetCore.Mvc.ViewComponent类 ,代码如下:

先添加引用

using Microsoft.AspNetCore.Mvc;

然后继承,如果需要依赖注入请按上一章节的方式注册服务和注入

     public class MessageBoxViewComponent : ViewComponent
    {
        //private readonly LogHandler _logHandler;
    
            //public MessageBoxViewComponent(LogHandler logHandler)//依赖注入日志操作服务实例,需要在Startup中注册
            //{
            //    this._logHandler = logHandler;
            //}
    }

接下来需要在控制器类中添加一个InvokeAsync函数,返回类型为IViewComponentResult(Task),该函数异步执行,可以自定义接收参数,我们可以在视图中为他传参

        public async Task<IViewComponentResult> InvokeAsync(MessageBoxVO options)//传入参数为MessageBoxVO类型的options(弹窗信息选项)
        {
            if (options == null || (string.IsNullOrEmpty(options.Msg) && string.IsNullOrEmpty(options.RedrectUrl)))
                return Content("");//如果弹窗信息选项为空或内容和跳转URL均为空,证明不跳转,该组件直接输出空内容即可
            //如果需要记录日志,可以在此进行操作如:await  _logHandler.CustomMessage(model);
            return View(options);//否则返回视图(一段弹窗控制的JS)
        }

MessageBoxVO类是弹窗组件的配置选项类,代码如下:

public class MessageBoxVO
    {
        public string Msg { get; set; }

        public string RedrectUrl { get; set; }


        public string Title { get; set; }
    }

创建ViewComponent视图
前文已经说了,组件的视图主要是一段控制弹窗的JS。按照约定,我们可以在项目视图目录(Views目录)下,创建一个名为“Components”的目录存放视图组件的视图,也可以在Views/Shared下(跟 _Layout一个目录,我习惯放在这个目录下)创建一个名为“Components”的目录存放视图组件的视图。

Components目录中需要按照组件的名称建立文件夹,目录结构如下:

我们的弹窗组件名称为“MessageBox”,所以需要在Components目录下再建立一个“MessageBox”目录。

我们的控制器中没有指定返回哪个视图,因此我们可以创建一个默认视图“Default.cshtml”。

该视图绑定的Model类型为MessageBoxVO

具体代码如下

@model WebApplication1.Models.MessageBoxVO
    <script>
        //这是一个JS自执行函数,在该函数创建完成后将会自动调用,因为页面的JS是按照顺序执行的,所以将它放在页面Body的最底部,将会在页面渲染完成自动执行
        !function (msg, redrectUrl) { //函数接收msg和redrectUrl两个参数,第一个为提示内容,第二个为跳转地址
            if (msg && msg.toString().length > 0)//如果存在msg则弹出
            {
                alert(msg);
            }
            if (redrectUrl && redrectUrl.toString().length > 0)//如果redrectUrl有内容则在弹窗被关闭(点确认)后会继续执行页面跳转
            {
                parent.location.href = redrectUrl;
            }
        }
            (
                //在这里给自执行函数传参
                @Html.Raw((Model == null ? "null" : "'"+Model.Msg+"'")),
                @Html.Raw(( Model == null ? "null" : "'" +Model.RedrectUrl+"'"))
            );
    </script>

引用组件

视图组件的引入方式是视图需要引入的地方使用Component.InvokeAsync方法引入,最终这个地方将会被组件的Html替换
Component.InvokeAsync方法的定义如下:

Task<IHtmlContent> InvokeAsync(string name, object arguments);//Component.InvokeAsync方法有两个参数,第一个参数为组件名称,即“MessageBox”,第二个参数是传入参数,是一个对象,可以使用new{…… }的方式创建一个匿名对象

我们在可以在视图模板中统一引入组件,这样每个页面都会在最底部自动引入MessageBox组件

打开_Layout.cshtml,在Body的最后用Component.InvokeAsync方法引入组件,

调用代码:

@await Component.InvokeAsync("MessageBox", new { model = ViewBag.SystemMessageBoxInfo == null ? null : ViewBag.SystemMessageBoxInfo as WebApplication1.Models.MessageBoxVO })

我们看其中这段代码

ViewBag.SystemMessageBoxInfo as WebApplication1.Models.MessageBoxVO

父页面的控制器将参数传入给组件我们采用的是ViewBag,如果需要弹出窗口,就在ViewBag创建一个名为“SystemMessageBoxInfo”的键,并将弹窗配置对象赋值给他即可

_Layout.cshtml页全部代码:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <environment include="Development">
        <link rel="stylesheet" asp-href-include="css/*" asp-href-exclude="css/all.min.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet"  asp-href-include="all.min.css" />
    </environment>
</head>
<body>
    <div>
        <img src="/img/logo.jpg" asp-append-verison="true" alt="Jxmaker.com" style="20%" />
    </div>
    <div>
        @RenderBody()
    </div>
    <!--弹窗组件引入-->
    @await Component.InvokeAsync("MessageBox", new { options= ViewBag.SystemMessageBoxInfo == null ? null : ViewBag.SystemMessageBoxInfo as WebApplication1.Models.MessageBoxVO })
    <!--弹窗组件引入结束-->
</body>
</html>

在Controller中控制页面弹出一个弹窗
在上一章中,我们创建控制器时有介绍控制器都是继承Microsoft.AspNetCore.Mvc.Controller类,我们可以写一个自定义的控制器基类,继承Microsoft.AspNetCore.Mvc.Controller类,并定义一个函数,取名为“MessageBoxView”。这个函数最终返回一个实现IActionResult的实例,并实现弹窗传值。

直接看控制器基类代码:

namespace WebApplication1.Code
{
    public class WebBaseController: Controller //Web视图控制器基类继承Microsoft.AspNetCore.Mvc.Controller
    {
        /// <summary>
        /// 返回对应视图并进行弹窗
        /// </summary>
        /// <param name="msg">提示信息,为null或空字符串则不弹窗</param>
        /// <param name="redrectUrl">跳转地址,为null或空字符串则不跳转</param>
        /// <returns></returns>
        protected IActionResult MessageBoxView(string msg, string redrectUrl=null)
        {
            ViewBag.SystemMessageBoxInfo = new MessageBoxVO()//通过ViewBag将弹窗配置信息传值给视图,视图再传值给MessageBox组件
            {
                Msg = msg,
                RedrectUrl = redrectUrl
            };
            return base.View();//返回对应视图
        }

        /// <summary>
        /// 返回对应视图并进行弹窗
        /// </summary>
        /// <typeparam name="T">视图绑定Model类型</typeparam>
        /// <param name="msg">提示信息,为null或空字符串则不弹窗</param>
        /// <param name="redrectUrl">跳转地址,为null或空字符串则不跳转</param>
        /// <param name="model">传入视图Model,为null则自动通过new创建一个Model实例传入给视图</param>
        /// <returns></returns>
        protected IActionResult MessageBoxView<T>(string msg, string redrectUrl=null,T model=default) where T:new ()
        {
            if (model == null)//如果传入视图的Model为Null则通过new创建一个Model实例传入给视图
                model = new T();
            ViewBag.SystemMessageBoxInfo = new MessageBoxVO()//通过ViewBag将弹窗配置信息传值给视图,视图再传值给MessageBox组件
            {
                Msg = msg,
                RedrectUrl = redrectUrl
            };
            return base.View(model);//返回对应视图
        }

    }
}

例1

当浏览职员列表页未指定部门时,提示”您没有权限查看全部职员,请按部门查找“,并跳转到部门列表页

我们需要弹窗的控制器类(EmployeeController)需要先继承自定义的基类(WebBaseController)

    public class EmployeeController:WebBaseController
    {
    }

修改控制器中Index的Action,判断是否有deptId传值(并判断是否合规),如果没有(或不合规)就提示

 public async Task<IActionResult> Index(int? deptId,string name,bool? isFired)
        {
            //判断是否指定部门或是否指定的部门id不合规,如果是就弹窗提示并跳转到部门列表
            if(!deptId.HasValue||deptId.Value<1)
                return MessageBoxView<List<EmployeeInfo>>("您没有权限查看全部职员,请按部门查找","../Dept/Index");
            ViewBag.DeptId = deptId;
            ViewBag.Title = (deptId.HasValue? (await _deptService.GetSingle(deptId.Value)).Name :"全部")+"成员列表";
            var list = await _employeeService.GetList(deptId, name, isFired);
            return View(list);
        }

例2

部门添加成功后需要弹窗提示添加成功,点确定后跳转到部门列表页

我们需要弹窗的控制器类需要先继承自定义的基类

public class DeptController:WebBaseController
{
}

修改该控制器下名为“Add"的Action

 public async Task<IActionResult> Add(DeptInfo model)
        {
            if (!ModelState.IsValid)
                return null;
            await _deptService.Add(model);
            //return RedirectToAction(nameof(Index));   //原来的写法
            return MessageBoxView<DeptInfo>(msg:"添加成功",model:model,redrectUrl:"Index");
        }

此时就能够实现在控制器中控制页面弹出提示框并在点击确定后跳转。

总结与思考
如此我们就完成了控制器中控制页面弹出提示框功能实现。

思考:

现在这种方式用户可以看到页面的内容,才会展示提示框,如果我不想让用户看到页面内容,直接弹出提示和跳转,要怎么做?

思考答案:

修改_Layout.cshtml,将组价引入移到<head>标签的结尾,因为html页面渲染是由上到下的,此时我们使用了自执行函数,渲染到这一步就会跳转,body中的页面内容还没渲染

教程Demo下载地址:
https://download.csdn.net/download/zinechina/12634702


————————————————
版权声明:本文为CSDN博主「红鲜森」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zinechina/article/details/107428180

原文地址:https://www.cnblogs.com/djd66/p/15250909.html