Abp mvc angular 添加视图

时间:2022-08-31 18:21:21

在LawAndRegulation项目中添加导航路由(Abp添加菜单)对应的客户端页面。

创建文件

客户端页面在Abp模板项目中默认存放在Abp/Main/views文件夹下,在项目中我们创建属于字典管理的新文件夹,名字命名为DictionaryManger。

在文件夹中创建文件index.cshtml和同名的js文件index.js。

创建的index.cshtml文件中只需要编写页面内容部分,idnex.js中编写当前页面逻辑。

创建index.cshtml需要注意:

  • 创建时选择“带有布局的MVC5视图页”选项,选择合适的母版(Views/Shared/_Layout.cshtml);
  • 删除原有内容;

同样添加index.js文件。

编辑index.cshtml

在视图页面中编写你的html内容,在Abp母版项目中已经引入了Bootstrap,项目DictionaryManager在编写页面内容时,使用Bootstrap作为前端开发框架。

在项目的Bundle.config文件中已经通过文件夹引入的方式引入了/App/Main文件夹下所有css文件所以页面编写需要编写的css可以直接在index.cshtml项目所在目录下创建文件添加,在本项目中我们添加了dicCommon.css文件。

页面中使用Bootstrap模态框,想要详细学习模态框,请移步这里,有详细的使用介绍。

css文件中定义了很多自定义样式,这些样式中的单位存在px和em,存在这样的问题是因为部分内容继承自原有代码,还没有时间进行统一更改,单位之间换算参考链接,这部分内容还没有进行验证。按照文中介绍,默认情况下1em=16px,但em是相对单位所以如果父元素定义font-size字段那么1em=父元素定义的大小。

添加html,代码如下:

<form id="dicForm" runat="server"  ng-controller="app.views.dictionarymanager.index as vm">
    <div class="row">
        <div class="col-xs-12 dic_header" id="dicHeaderBtn">
            <button type="button" class="btn btn-primary  pull-left" id="btnAddDicCategory" ng-disabled="btnAddDicCategoryDisabled">
                新增字典分类
            </button>
            <button type="button" class="btn btn-primary pull-left" id="btnAddDic" ng-disabled="btnAddDicDisabled" ng-click="AddDictinary()">
                新增字典
            </button>
            <button type="button" class="btn btn-primary pull-left" id="btnEditDic" ng-disabled="btnEditDicDisabled">
                编辑
            </button>
            <button type="button" class="btn btn-primary pull-left" id="btnDeleteDic" ng-disabled="btnDeleteDicDisabled">
                删除
            </button>
            <button type="button" class="btn btn-primary pull-left" id="btnExport" ng-disabled="btnExportDisabled">
                导出
            </button>
            <button type="button" class="btn btn-primary pull-left" id="btnImport">
                导入
            </button>
        </div>
        <div class="col-xs-12 d-p-l">
            <div class="col-sm-4 col-lg-3 dic_noderow" id="dicNodeRow">
                <div id="dicTree">
                </div>
            </div>
            <div class="col-sm-8 col-lg-9 d-p-l" id="divDetail">
                <iframe id="showInfomation" ng-src="{{srcValue}}" name="iframeSelf"></iframe>
            </div>
        </div>
    </div>
</form>

上面代码为bootstrap(css/html开源框架),其中dic-header、d-p-l、dic-noderow为自定义css其他为bootstrap原生要素及属性,如果想进一步学习bootstrap使用可以查看链接。上面代码除了使用布局及按钮之外,还使用了模态框,关于模态框菜鸟有介绍,移步链接。class内包含col-sm-12等col开头的样式,这些样式为bootstrap的网格系统,网格系统的具体介绍请查看上面bootstrap链接。

视图中涉及到的样式

bootstrap默认样式介绍:

  • btn:原始按钮样式;
  • btn-primary:基本按钮样式;
  • pull-left:整体左滑动;类似于左对齐,比如页面中控件隐藏,则右侧控件像左侧滑动;
  • col-xm-12在小型设备上的布局,视图总计为12,class中存在这个预定义类的要素相当于充满整个屏幕;
  • col-lg-3为在大型设备上布局,结合上面的col-xm-使用就可以达到响应式(一个站点适应多个终端)的布局;

这些默认样式具体定义了什么可以查看bootstrap-theme.css(可以通过下载bootstrap框架得到这个文件)中源码进行查看。

自定义样式:

.r-p-l {
    padding-left: 10px;
}
div.col-xs-12.dic_header {
    padding-top: 10px;
    margin-left: 15px;
    padding-bottom: 50px;
}
div.dic_noderow, #dicTree {
    overflow-x: hidden;
    overflow-y: auto;
    max-width: 500px;
}

自定义样式中我们定义了以下属性:

  • padding-left、padding-top、padding-bottom:盒子模型的内边距;
  • margin-left:盒子模型的外边距;
  • overflow-x、overflow-y:内容溢出是否裁剪;

视图中涉及的标签

  • button:按钮标签;
  • form:负责数据采集的一组标签;
  • div:将视图划分为不同区域;

还有其他bootstrap自定义样式,这里不再详细介绍,如果有时间开设专门的博客介绍bootstrap。

要素包含ng-字符的属性属于angular,这些属性大部分把ng-去掉即为要素原生属性,我们在视图中添加这些属性,就可以在控制器中直接对这些属性进行赋值,下一节我们详细介绍这些属性的使用。

编辑index.js

立即执行函数-如果已经掌握跳过

首先在inde.js文件中编写一个立即执行函数,立即执行函数就是在函数定义的地方直接执行这个函数。作用及好处可以归纳为隔离作用域,在私有作用域内变量及函数不会被污染,相当于起到命名空间的作用。

编写方式有以下几种(详细了解参考链接):

//user () operational character
(function(){})();
(function(){}());
//user ! operational character
!(function(){})();
//uer + operational character
+(function(){})();
//uer + operational character
-(function(){})();
//uer + operational character
var temp=(function(){})();

在我们的js文件中我们使用了第一种方式编写以及执行函数。

angular模块module

在js文件的立即执行函数中,我们首先定义一个模块,在angularjs中模块是应用程序中不同部分的容器,在这些容器中我们可以添加controllers(控制器)、services(服务)、filters(过滤器)、directives(指令)等内容。

angular中模块通过angular.mudole(name,requires,configfn)方法创建:

  • name:模块的名称,与视图中html标签的ng-app属性相关联;
  • requires:模块的依赖关系,如果在调用方法时没有设置此参数,那么方法默认为获取这个模块;如果创建新模块并确定没有依赖关系,那么需要设置这个参数为[]数组;
  • configfn:负责在模块初始化时做一些配置,参数是方法或者数组,这个参数是数组,那么最后一个参数必须是方法。
angular控制器controller

控制器是对视图的数据和时间处理函数进行挂载,同时进行一定的业务功能的底层封装和处理,起到的作用就是增强视图,在视图的作用于中增加额外的功能,我们用它来给作用于对象设置初始化状态,添加自定义行为。

创建完成angular模块后,调用模块方法controller创建控制器,controller的参数组成为:

  • controllerName:控制器名称,控制器名称和ag-controller属性值相对应;
  • controllerConstructor:第二个参数为一个回调函数,用于执行构造函数初始化内容;
angular作用域scope

在项目代码中,controller的参数是一个scope对象,scope是视图与控制器之间的纽带,scope是一个javascript对象,scope可以定义属性和方法,这些属性和方法可以在视图和控制器之间使用。

调用标签事件

通用事件

//angular
angular.element("#id").click();
//jquery
$("#id").click();
//原生
document.getElementById("#id").trigger("click");

标签特定事件

//angular
angular.element("#id").on("eventName",function(event,data){});
//jquery
$("#id").on("eventName",function(event,data){});
基于bootstrap的jQuery多级列表树插件

装载及初始化插件:

            //初始化treeview
            angular.element("#dicTree").treeview({
                data: getDictinary(),
                levels: 2,
                color: '#000000',
                backColor: '#FFFFFF',
                href:'#node-1'
            });
function getDictinary() {
            var tree = [
                {
                    text: "Parent 1",
                    nodes: [{text: "Child 1",nodes: [{text: "Grandchild 1"}]}]
                }
            ];
            return tree;
        }

以上为最简单结构,还有其他扩展属性可以查看链接,节点属性一节中有详细的介绍。

添加treeview控件的节点选择事件:

            angular.element("#dicTree").on('nodeSelected', function (event, data) {
                alert(data.text);
            });

当节点被选择后可以触发事件调用该方法,在本项目中为了显示节点的详细信息。

持久化当前选择节点内容

所谓的持久化内容,也就是在当前视图的生命周期内保存视图中需要的内容。

在节点选择事件中对视图中form表单的隐藏标签赋值,首先添加隐藏标签到index.cshtml中:

    <input type="hidden" id="currentNodeText" />
    <input type="hidden" id="currentNodeId" />
    <input type="hidden" id="currentHref" />

在节点改变事件绑定的方法中赋值:

            angular.element("#dicTree").on('nodeSelected', function (event, data) {
                angular.element("#currentNodeText").val(data.text);
                angular.element("#currentNodeId").val(data.id);
            });

存储当前选择的节点是为了在视图作用域内持久化节点内容。

组件化模块(angular component)

解耦复杂系统时将多个功能模块拆分、重组的过程就叫做模块化,其中有多个属性、状态描述其内部特性。

组件化与传统的编写方式相比较,有什么优势呢,或者叫解决了什么问题,根据园内CooMark的总结如下:

  • app中的某个部分如何重用(在这里我们可以叫做,视图内的某个模块如何重用);
  • app中的scope作用域不是隔离的,变量和方法存在污染的可能;
  • 解决了传统模式不能重用的问题;
  • 简化了视图中的标签,便于维护;
  • 组件间是隔离的,互不影响;
  • 可以独立测试组件;

使用component:

  • 文件名建议包含.component.js
  • component命名采用驼峰式命名法,在视图中使用时采用“-”进行分割;
  • template使用controller的实例$ctrl替代scope访问数据,别名可以使用controllerAs进行自定义;
  • component内的controller可以通过controller方法进行定义;
  • templateUrl定义时需要注意,根目录为当前视图目录(如果是mvc项目,那么根目录就是母版目录_Layout.cshtml);

定义模块:

    var controllerId = 'app.views.dictionarymanager.index';
    var myModule = angular.module("app");

注册组件到模块:

  • template:元素组件本身;
  • templateUrl:通过文件方式定义元素;
  • controller:定义组件controller,这样组件内就可以通过默认的$ctrl方式调用数据;
  • bingdings:绑定数据到元素,其中<为单向绑定,父作用域的变化影响子作用域;
  • scope:组件内单独的作用域;
  • controllerAs:重命名控制器;
  • restrict:字符型,E 表示该指令是一个element; A 表示该指令是attribute; C 表示该指令是class; M 表示该指令是注视;
    myModule.component('myComp', {
        //template: '<div>My name is {{$ctrl.text}}</div>',
        templateUrl:'././App/Main/views/dictionarymanager/dicItem.html',
        controller: function () {
            this.name = 'shahar';
        },
        bindings: {
            text:'<'
        }

 其中注册组件可以通过两种方式引入模板,第一种通过直接编写的方式,也就是代码中template的内容,也可以通过文件引入的方式,如图中templateUrl的方式。

视图中引入组件,视图中通过-分割驼峰式命名法的组件,并定义绑定的属性,绑定的属性可以通过controller的别名调用父scope内的数据进行绑定:

<my-comp text="vm.dicMainInfo"></my-comp>

父组件向组件传递数据:

myModule.controller(controllerId, ['$scope', function ($scope) {
        this.dicMainInfo = 'hello';
    }]);

indes.js代码如下:

(function () {
    var controllerId = 'app.views.dictionarymanager.index';
    angular.module("app").controller(controllerId, ['$scope', function ($scope) {
        $scope.AddDictinary = function () {
            alert("hello");
            $scope.btnADCDis = false;
        };
        function initialize() {
            $scope.srcValue = "";
            $scope.btnAddDicCategoryDisabled = true;
            $scope.btnAddDicDisabled = true;
            $scope.btnEditDicDisabled = true;
            $scope.btnDeleteDicDisabled = true;
            $scope.btnExportDisabled = true;
        }
        initialize();
    }]);
})();

在上面的代码中实践了以上介绍的知识点,首先定义controllerId变量,窗体与视图中ag-controller属性相同的值,通过module定义模块,因为在mvc项目中app的div也就是区域已经在模板视图中定义,所以在定义模块时统一都是用app名称进行定义,再通过调用模块的controller方法创建控制器,控制器中传递scope参数,在回调函数中编写初始化代码及逻辑代码,作用域(scopde)中定义的属性及方法与视图中的属性及事件相关联,这样即实现了初始化属性又装载了事件方法。

错误处理

TypeError: $.fn.bootstrapTable is undefined, bootstrap-table-zh-CN.js文件加载错误是因为加载顺序造成的,加载顺序遵循首先加载bootstrap.js文件,然后加载bootstrap-table.js文件,最后加载 bootstrap-table-zh-CN.js文件。