基于ng-zorro的ASP.NET ZERO前端实现之代码生成器

上一篇介绍了集成ng-zorro的过程,本篇我们来看下如何用abp官方的生成器来生成前端代码。

Abp官方提供了一个强大的代码生成器 ASP.NET Zero Power Tools,它的Visual Studio 插件在这里。当然你也可以不用插件,但你得自己创建json文件。相关官方文档见这里。
基于ng-zorro的ASP.NET ZERO前端实现之代码生成器_第1张图片

工作原理

生成器(vs插件)首先根据用户填写的Entity相关的内容创建一个json描述文件,然后开始将真正的代码生成核心程序释放到aspnet-core\AspNetZeroRadTool这个目录下,然后开始执行生成,命令行:dotnet AspNetZeroRadTool.dll YourEntity.Json。执行过程中会扫描FileTemplatesn内的文件进行实际的文件替换等操作生成最终代码文件。

AspNetZeroRadTool文件夹

基于ng-zorro的ASP.NET ZERO前端实现之代码生成器_第2张图片

除了config.json其余内容均可以从生成器(vs插件)自动释放出来。所以你如果用git其实可以把除config.json外的文件ignore。

FileTemplates 文件夹

FileTemplates文件夹顾名思义是放了一些模板文件。本次我们只关注Angular模板,目录结构如下:
基于ng-zorro的ASP.NET ZERO前端实现之代码生成器_第3张图片

  • ComponentHtmlTemplate:用于生成 Asp.net Zero 5.4之前的component html
  • ComponentTemplate:用于生成component ts
  • ComponentTurboTableHtmlTemplate:用于生成 Asp.net Zero 5.4之后的componnet html (由于5.4之后Table更换成了TurboTable组件)
  • CreateOrEditComponentHtmlTemplate:用于生成创建或修改对话框的html
  • CreateOrEditComponentTemplate:用于生成创建或修改对话框的ts
  • LookupTables:用于生成选择关联实体对话框的html/ts (如果你定义的实体有外键关联,在编辑或创建时会通过该对话框进行选择)
  • ViewEntityComponentHtmlTemplate:用于生成view only对话框html (插件界面中有可选项)
  • ViewEntityComponentTemplate:用于生成view only对话框ts (插件界面中有可选项)

每一个模板文件夹都包含以下三个文件,我们来看一下:
基于ng-zorro的ASP.NET ZERO前端实现之代码生成器_第4张图片

  • MainTemplate.txt:根据所在文件夹的不同有不同内容(html/ts),其中会有一些占位的字符串,它们以大括号{{...}}包裹。在生成阶段会被替换。有几个比较通用的占位字符串:
    {{Entity_Name_Here}}表示首字母大写的实体名称,如Book;
    {{entity_Name_Here}}表示首字母小写的实体名称,如book。
  • PartialTemplates.txt:MainTemplate.txt中会有一些占位字符串的内容会根据条件从这个文件中获取。比如生成器在处理MainTemplate.txt时遇到{{Get_Excel_Method_Here}}这个占位符时会去PartialTemplates.txt文件里找到对应的内容,然后根据PartialTemplates的内容(有条件的)去填充MainTemplate.txt的内容。
  • TemplateInfo.txt:这个文件很简单,里面只有path和condition。path代表最终生成之后文件的位置;condition代表是否需要按照本目录的模板生成代码。

创建我们自己的模板

了解了以上结构之后,我们来创建我们的模板文件。

首先我们来禁用一些我们用不到的模板目录,禁用方式很简单,在目录中我们只要复制相应的TemplateInfo.txt文件并重命名为TemplateInfo.custom.txt,然后修改里面的condition为"condition":"false"即可。我们需要禁用ComponentTurboTableHtmlTemplateLookupTables\LookupTableComponentTurboTableHtmlTemplateLookupTables\LookupTableCssTemplate这几个文件夹。

以下我们创建一个列表显示页的html模板页:

  1. 创建模板目录ComponentNgZorroTableHtmlTemplate
  2. 创建模板三个文件MainTemplate.txt、PartialTemplates.txt、TemplateInfo.txt

MainTemplate.txt的内容:


    
        {{l("{{Entity_Name_Plural_Here}}")}}
        
            
            {{l("{{Entity_Name_Plural_Here}}HeaderInfo")}}
        
    


    
{{Property_Filter_Template_Here}}{{NP_Filter_Template_Here}}
{{Get_Excel_Button_Here}} {{advancedFiltersVisible ? l('HideAdvancedFilters') : l('ShowAdvancedFilters')}}
{{selectedDataItems.length}} items selected {{l('Clear')}} {{l('Refresh')}}
{{l('TotalRecordsCount', total)}} {{NP_Looped_Header_Template_Here}}{{Property_Looped_Header_Template_Here}} {{l('Actions')}} {{NP_Looped_Template_Here}}{{Property_Looped_Template_Here}} {{View_Button_Here}} {{l('Edit')}} {{l('Delete')}}

PartialTemplates.txt的内容:

{
"propertyTemplates":[
		{
			"placeholder" : "{{Property_Looped_Header_Template_Here}}",
			"condition" : "{{Property_Listed_Here}} == true",
			"templates" : [
					{
					"type" : "default",
					"content" : "
							
								{{l('{{Property_Name_Here}}')}}
							
						"
					}
				]
		},
		{
			"placeholder" : "{{Property_Looped_Template_Here}}",
			"condition" : "{{Property_Listed_Here}} == true",
			"templates" : [
					{
					"type" : "enum",
					"content" : "
					
						{{l('Enum_{{Property_Type_Here}}' + {{property_Type_Here}}[item.{{entity_Name_Here}}.{{property_Name_Here}}])}}
                    "
					},
					{
					"type" : "bool",
					"content" : "
						
							{{l('Yes')}}
							{{l('No')}}
						"
					},
					{
					"type" : "DateTime",
					"content" : "
						
							{{item.{{entity_Name_Here}}.{{property_Name_Here}} | momentFormat:\'L\'}}
						"
					},
					{
					"type" : "default",
					"content" : "
						
							{{item.{{entity_Name_Here}}.{{property_Name_Here}}}}
						"
					}
				]
		},
		{
			"placeholder" : "{{Property_Filter_Template_Here}}",
			"condition" : "{{Property_Advanced_Filter_Here}} == true",
			"templates" : [
					{
					"type" : "enum",
					"content" : "
							
								
									
										{{l(\"{{Property_Name_Here}}\")}}
									
									
										
										
										{{Enum_Option_Looped_Template_Here}}
									
									
								
							"
					},
					{
					"type" : "bool",
					"content" : "
							
								
									
										{{l(\"{{Property_Name_Here}}\")}}
									
									
										
										
										
										
									
									
								
							"
					},
					{
					"type" : "DateTime",
					"content" : "
							
								
									
										{{l(\"{{Property_Name_Here}}\")}}
									
									
										
									
								
							"
					},
					{
					"type" : "numeric",
					"content" : "
							
								
									
										{{l(\"{{Property_Name_Here}}\")}}
									
									
										
										
									
								
							"
					},
					{
					"type" : "default",
					"content" : "
							
								
									
										{{l(\"{{Property_Name_Here}}\")}}
									
									
										
									
								
							"
					}
				]
		}
	],
"navigationPropertyTemplates":[
	{
			"placeholder" : "{{NP_Looped_Header_Template_Here}}",
			"templates" : [
					{
					"relation" : "single",
					"content" : "
								
									{{l('{{NP_Display_Property_Name_Here}}')}}
								
							"
					}
				]
		},
		{
			"placeholder" : "{{NP_Looped_Template_Here}}",
			"templates" : [
					{
					"relation" : "single",
					"content" : "
					
                        {{item.{{nP_Foreign_Entity_Name_Here}}{{NP_Display_Property_Name_Here}}{{NP_Duplication_Number_Here}}}}
                    "
					}
				]
		},
		{
			"placeholder" : "{{NP_Filter_Template_Here}}",
			"templates" : [
					{
					"relation" : "single",
					"content" : "
							
								
									
										({{l(\"{{NP_Foreign_Entity_Name_Here}}{{NP_Duplication_Number_Here}}\")}}) {{l(\"{{NP_Display_Property_Name_Here}}\")}}
									
									
										
									
								
							"
					}
				]
		}
	],
"enumTemplates":[
		{
			"placeholder" : "{{Enum_Option_Looped_Template_Here}}",
			"content" : "
			"
		}
	],
"conditionalTemplates":[
		{
			"placeholder": "{{View_Button_Here}}",
			"condition": "{{Create_View_Only_Here}} == true",
			"content": "
					
                            
                                
                                {{l('View')}}
                            
                            
                        "
		},
		{
			"placeholder": "{{Get_Excel_Button_Here}}",
			"condition": "{{Create_Excel_Export_Here}} == true",
			"content": ""
		}
	]
}

TemplateInfo.txt的内容:

{
	"path" : "app\\{{menu_Position_Here}}\\{{namespace_Relative_Here}}\\{{entity_Name_Plural_Here}}\\{{entity_Name_Plural_Here}}.component.html",
	"condition": "true"
}

创建列表显示的typescript文件:
ts模板目录我们直接使用已有的模板目录ComponentTemplate, 这里需要注意如需要修改默认的模板文件内容,必须复制该文件并重命名成.custom.txt。比如复制MainTemplate.txt为MainTemplate.custom.txt,之后在MainTemplate.custom.txt添加你自己的模板内容。
修改后的MainTemplate.custom.txt内容:

import { Component, Injector } from '@angular/core';
import { {{Entity_Name_Plural_Here}}ServiceProxy, {{Entity_Name_Here}}Dto {{Enum_Import_Here}}, Get{{Entity_Name_Here}}ForView } from '@shared/service-proxies/service-proxies';
import { PagedListingComponentBase, PagedRequestDto } from '@shared/common/paged-listing-component-base';
import { CreateOrEdit{{Entity_Name_Here}}ModalComponent } from './create-or-edit-{{entity_Name_Here}}-modal.component';{{View_Component_Import_Here}}
{{View_Component_Import_Here}}
import { FileDownloadService } from '@shared/utils/file-download.service';

import { finalize } from 'rxjs/operators';
import * as moment from 'moment';
import * as _ from 'lodash';

@Component({
    templateUrl: './{{entity_Name_Plural_Here}}.component.html'
})
export class {{Entity_Name_Plural_Here}}Component extends PagedListingComponentBase {

    advancedFiltersAreShown = false;
	filterText = '';
{{Property_Filter_Def_Here}}{{NP_Filter_Def_Here}}
	{{enum_Def_Here}}

    constructor(
        injector: Injector,
        private _{{entity_Name_Plural_Here}}ServiceProxy: {{Entity_Name_Plural_Here}}ServiceProxy,
        private _fileDownloadService: FileDownloadService
    ) {
        super(injector);
    }

    protected fetchDataList(request: PagedRequestDto, pageNumber: number, finishedCallback: () => void): void {
        this._{{entity_Name_Plural_Here}}ServiceProxy
            .getAll(
                this.filterText,{{Property_Filter_Param_Here}}{{NP_Filter_Param_Here}}
                request.sorting,
                request.skipCount,                
                request.maxResultCount
            )
            .pipe(finalize(finishedCallback))
            .subscribe(result => {
                this.dataList = result.items;
                this.showPaging(result);
            });
    }

    createOrEdit(id?: number): void {
        this.modalHelper
            .createStatic(CreateOrEdit{{Entity_Name_Here}}ModalComponent, { {{entity_Name_Here}}Id: id }, { size: 'md' })
            .subscribe(res => {
                if (res) {
                    this.refresh();
                }
            });
    }

    delete{{Entity_Name_Here}}({{entity_Name_Here}}: {{Entity_Name_Here}}Dto): void {
        this._{{entity_Name_Plural_Here}}ServiceProxy.delete({{entity_Name_Here}}.id)
            .subscribe(() => {
                this.refresh();
                this.notify.success(this.l('SuccessfullyDeleted'));
            });
    }

    {{Get_View_Component_Method_Here}}
	{{Get_Excel_Method_Here}}
}

PartialTemplates.custom.txt内容:

{
"propertyTemplates":[
		{
			"placeholder" : "{{Property_Filter_Def_Here}}",
			"condition" : "{{Property_Advanced_Filter_Here}} == true",
			"templates" : [
					{
					"type" : "enum",
					"content" : "		{{property_Name_Here}}Filter = -1;
"
					},
					{
					"type" : "byte",
					"content" : "		max{{Property_Name_Here}}Filter : string = '';
		min{{Property_Name_Here}}Filter : string = '';
"
					},
					{
					"type" : "numeric",
					"content" : "		max{{Property_Name_Here}}Filter : number;
		max{{Property_Name_Here}}FilterEmpty : number;
		min{{Property_Name_Here}}Filter : number;
		min{{Property_Name_Here}}FilterEmpty : number;
"
					},
					{
					"type" : "DateTime",
					"content" : "		max{{Property_Name_Here}}Filter : moment.Moment;
		min{{Property_Name_Here}}Filter : moment.Moment;
"
					},
					{
					"type" : "bool",
					"content" : "		{{property_Name_Here}}Filter = -1;
"
					},
					{
					"type" : "default",
					"content" : "		{{property_Name_Here}}Filter = '';
"
					}
				]
		},
		{
			"placeholder" : "{{Property_Filter_Param_Here}}",
			"condition" : "{{Property_Advanced_Filter_Here}} == true",
			"templates" : [
					{
					"type" : "byte",
					"content" : "
			this.max{{Property_Name_Here}}Filter == null ? '' : this.max{{Property_Name_Here}}Filter,
			this.min{{Property_Name_Here}}Filter == null ? '' : this.min{{Property_Name_Here}}Filter,"
					},
					{
					"type" : "numeric",
					"content" : "
			this.max{{Property_Name_Here}}Filter == null ? this.max{{Property_Name_Here}}FilterEmpty: this.max{{Property_Name_Here}}Filter,
			this.min{{Property_Name_Here}}Filter == null ? this.min{{Property_Name_Here}}FilterEmpty: this.min{{Property_Name_Here}}Filter,"
					},
					{
					"type" : "DateTime",
					"content" : "
			this.max{{Property_Name_Here}}Filter,
			this.min{{Property_Name_Here}}Filter,"
					},
					{
					"type" : "default",
					"content" : "
			this.{{property_Name_Here}}Filter,"
					}
				]
		}
	],
"navigationPropertyTemplates":[
		{
			"placeholder" : "{{NP_Filter_Def_Here}}",
			"templates" : [
					{
					"relation" : "single",
					"content" : "		{{nP_Foreign_Entity_Name_Here}}{{NP_Display_Property_Name_Here}}{{NP_Duplication_Number_Here}}Filter = '';
"
					}
				]
		},
		{
			"placeholder" : "{{NP_Filter_Param_Here}}",
			"templates" : [
					{
					"relation" : "single",
					"content" : "
			this.{{nP_Foreign_Entity_Name_Here}}{{NP_Display_Property_Name_Here}}{{NP_Duplication_Number_Here}}Filter,"
					}
				]
		}
	],
"enumTemplates":[
		{
			"placeholder" : "{{Enum_Import_Here}}",
			"content" : ", {{Entity_Name_Here}}Dto{{Enum_Used_For_Property_Name_Here}}"
		},
		{
			"placeholder" : "{{enum_Def_Here}}",
			"content" : "{{enum_Name_Here}} = {{Entity_Name_Here}}Dto{{Enum_Used_For_Property_Name_Here}};
		"
		}
	],
"conditionalTemplates":[
		{
			"placeholder": "{{View_Component_Import_Here}}",
			"condition": "{{Create_View_Only_Here}} == true",
			"content": "
import { View{{Entity_Name_Here}}ModalComponent } from './view-{{entity_Name_Here}}-modal.component';"
		},	
		{
			"placeholder": "{{Get_Excel_Method_Here}}",
			"condition": "{{Create_Excel_Export_Here}} == true",
			"content": "exportToExcel(): void {
        this._{{entity_Name_Plural_Here}}ServiceProxy.get{{Entity_Name_Plural_Here}}ToExcel(
		this.filterText,{{Property_Filter_Param_Here}}{{NP_Filter_Param_Here}}
		)
        .subscribe(result => {
            this._fileDownloadService.downloadTempFile(result);
         });
    }"
		},
		{
			"placeholder": "{{Get_View_Component_Method_Here}}",
			"condition": "{{Create_View_Only_Here}} == true",
			"content": "view{{Entity_Name_Here}}(id): void {
				this.modalHelper
					.create(View{{Entity_Name_Here}}ModalComponent, {
						{{entity_Name_Here}}Id: id
					}, { size: 'md' })
					.subscribe(() => { });
			}"
		}
	]
}

按照上述过程修改其他模板目录中的内容即可使用代码生成器按照你的模板进行代码生成。

在我的项目里已经包含了修改好的模板文件,请自行查看。

项目地址:https://github.com/rqx110/abp-ng-zorro

觉得还可以,请不要吝啬你的Star

使用

我们来简单测试下生成:
基于ng-zorro的ASP.NET ZERO前端实现之代码生成器_第5张图片
这里注意到几个带“Customized”的就是我们将默认模板自定义后的结果。

再来看一个生成后的ts文件,可以和上面的模板文件进行对比:

import { Component, Injector } from '@angular/core';
import { BooksServiceProxy, BookDto , GetBookForView } from '@shared/service-proxies/service-proxies';
import { PagedListingComponentBase, PagedRequestDto } from '@shared/common/paged-listing-component-base';
import { CreateOrEditBookModalComponent } from './create-or-edit-book-modal.component';
import { ViewBookModalComponent } from './view-book-modal.component';

import { ViewBookModalComponent } from './view-book-modal.component';
import { FileDownloadService } from '@shared/utils/file-download.service';

import { finalize } from 'rxjs/operators';
import * as moment from 'moment';
import * as _ from 'lodash';

@Component({
    templateUrl: './books.component.html'
})
export class BooksComponent extends PagedListingComponentBase {

    advancedFiltersAreShown = false;
	filterText = '';
		nameFilter = '';
		authorFilter = '';

	

    constructor(
        injector: Injector,
        private _booksServiceProxy: BooksServiceProxy,
        private _fileDownloadService: FileDownloadService
    ) {
        super(injector);
    }

    protected fetchDataList(request: PagedRequestDto, pageNumber: number, finishedCallback: () => void): void {
        this._booksServiceProxy
            .getAll(
                this.filterText,
			this.nameFilter,
			this.authorFilter,
                request.sorting,
                request.skipCount,                
                request.maxResultCount
            )
            .pipe(finalize(finishedCallback))
            .subscribe(result => {
                this.dataList = result.items;
                this.showPaging(result);
            });
    }

    createOrEdit(id?: number): void {
        this.modalHelper
            .createStatic(CreateOrEditBookModalComponent, { bookId: id }, { size: 'md' })
            .subscribe(res => {
                if (res) {
                    this.refresh();
                }
            });
    }

    deleteBook(book: BookDto): void {
        this._booksServiceProxy.delete(book.id)
            .subscribe(() => {
                this.refresh();
                this.notify.success(this.l('SuccessfullyDeleted'));
            });
    }

    viewBook(id): void {
				this.modalHelper
					.create(ViewBookModalComponent, {
						bookId: id
					}, { size: 'md' })
					.subscribe(() => { });
			}
	exportToExcel(): void {
        this._booksServiceProxy.getBooksToExcel(
		this.filterText,
			this.nameFilter,
			this.authorFilter,
		)
        .subscribe(result => {
            this._fileDownloadService.downloadTempFile(result);
         });
    }
}

Update (20190426)

生成完成后,在你的adminModule或mainModule里可能会默认生成了一些primeNG的东西,请手动删除。并且请把createOrEdit***ModalComponent加入到module的entryComponents中。

结束

ASP.NET Zero Power Tools是一个商业产品,你必须购买才能使用。

你可能感兴趣的:(基于ng-zorro的ASP.NET ZERO前端实现之代码生成器)