前,我內置表單功能中,已有自動關聯字典數據方式。由于,此前字典數據過于依賴表單字段,導致內存占用較高,字典數據重復性較大,字典緩存清理不便捷。
故,我欲改造之,將其與表單字段,單獨剝離,并統一與功能對象進行關聯,從而解決如上問題。
后端,需設計“功能字典關聯表”用于查詢,獨立“字典配置”注解,改造功能管理類。
前端,需提供通用字典獲取函數,改造使用字典的元素如(下拉框、單選框、多選框等)。
整體看來,此次改造還是比較簡單的,接下來就直接進入編碼環節。
package com.flycoding.drivenlibrary.engine.function.entity;
import com.flycoding.dblibrary.annotation.create.Column;
import com.flycoding.dblibrary.annotation.create.PrimaryAuto;
import com.flycoding.dblibrary.annotation.create.Table;
import com.flycoding.dblibrary.enums.ColumnType;
import com.flycoding.drivenlibrary.engine.constants.SqlConstants;
import com.flycoding.drivenlibrary.engine.function.entity.base.BaseFunctionMessage;
/**
* 功能字典關聯表
*
* @author 趙屈犇
* @version 1.0
* @date 創建時間: 2022/10/18 20:47
* @Copyright(C): 2022 by 趙屈犇
*/
@Table(tableName="Sy_Func_Dict")
public class FuncDictInfo extends BaseFunctionMessage {
/** 主鍵 */
@PrimaryAuto
private Integer id;
/** 普通字典編碼 */
@Column(columnName="dict_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dictCode;
/** 數據字典編碼 */
@Column(columnName="data_dict_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dataDictCode;
/** 字典驅動庫關聯配置編碼 */
@Column(columnName="db_config_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dbConfigCode;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id=id;
}
public String getDictCode() {
return dictCode;
}
public void setDictCode(String dictCode) {
this.dictCode=dictCode;
}
public String getDataDictCode() {
return dataDictCode;
}
public void setDataDictCode(String dataDictCode) {
this.dataDictCode=dataDictCode;
}
public String getDbConfigCode() {
return dbConfigCode;
}
public void setDbConfigCode(String dbConfigCode) {
this.dbConfigCode=dbConfigCode;
}
}
此對象,用于功能與字典之間的關聯。當,用戶獲取功能時,后臺通過此函數,自動裝載入字典數據。
package com.threeox.drivenlibrary.engine.annotation.function.form.field;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典配置
*
* @author 趙屈犇
* @version 1.0
* @date 創建時間: 2022/10/18 20:18
* @Copyright(C): 2022 by 趙屈犇
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
public @interface DictConfig {
/**
* 普通字典編碼
*
* @return
*/
String dictCode() default "";
/**
* 數據字典編碼
*
* @return
*/
String dataDictCode() default "";
/**
* 字典驅動庫關聯配置
*
* @return
*/
String dbConfigCode() default "";
/**
* 是否使用配置
*
* @return
*/
boolean isUseConfig() default true;
}
此注解,暫用于表單字段與字典配置的關聯函數。
可以看到,字典數據已于功能進行綁定。
接下來,我準備將字典數據,直接綁定至功能對象上,并返回給前端。
為提升查詢效率, 此處仍采用初次查詢,后續緩存的方式。
package com.threeox.drivenlibrary.engine.function.factory.config;
import com.threeox.drivenlibrary.cache.impl.function.FuncDictCacheManager;
import com.threeox.drivenlibrary.engine.constants.config.request.ConfigRequestConstants;
import com.threeox.drivenlibrary.engine.function.entity.FuncDictInfo;
import com.threeox.drivenlibrary.engine.function.factory.base.BaseCustomFactory;
import com.threeox.drivenlibrary.engine.request.execute.ExecuteRequestFactory;
import java.util.List;
/**
* 功能字典關聯工廠
*
* @author 趙屈犇
* @version 1.0
* @date 創建時間: 2022/10/20 20:42
* @Copyright(C): 2022 by 趙屈犇
*/
public class FuncDictFactory extends BaseCustomFactory<List<FuncDictInfo>> {
private static FuncDictFactory inst=null;
public static FuncDictFactory getInstance() {
if (inst==null) {
synchronized (FuncDictFactory.class) {
if (inst==null) {
inst=new FuncDictFactory();
}
}
}
return inst;
}
private FuncDictFactory() {
}
@Override
protected void init() {
}
@Override
protected void initCacheManager() {
cache=FuncDictCacheManager.getInstance();
}
@Override
protected List<FuncDictInfo> initResult(ExecuteRequestFactory requestFactory, String dbConfigCode, Object key, Integer relatedId, Object... extendParams) throws Exception {
// 查詢關聯字典配置根據功能主鍵
return getResult(requestFactory, ConfigRequestConstants.QUERY_FUNC_DICT_BY_FUNC_ID, relatedId);
}
}
最后,我在獲取功能對象時,動態獲取字典配置,并綁定字典結果數據。
為兼容普通字典和數據字典主鍵可能重復問題,我根據dictCode,dataDictCode,dbConfigCode進行拼接,生成新的key值。
/**
* 初始化功能字典數據
*
* @param functionInfo
* @return a
* @author 趙屈犇
* @date 創建時間: 2022/10/20 20:58
* @version 1.0
*/
private void initFuncDictData(FunctionMessage functionInfo) {
if (functionInfo !=null) {
// 獲取關聯字典數據
List<FuncDictInfo> dictInfos=funcDictFactory.getResult(functionInfo.getDbConfigCode(), functionInfo.getFuncCode(), functionInfo.getFuncId());
if (ArrayUtils.isNotEmpty(dictInfos)) {
Map <String, List<DictDataInfo>> dictMap=new HashMap<>();
dictInfos.forEach(dictInfo - > {
// key
String key=StringUtils.appendStrings(dictInfo.getDictCode(), dictInfo.getDataDictCode(), dictInfo.getDbConfigCode());
if (!dictMap.containsKey(key)) {
List<DictDataInfo> dictionaryInfos=null;
// 普通字典
if (StringUtils.isNotEmpty(dictInfo.getDictCode())) {
// 普通字典類型
dictionaryInfos=dictFactory.getResult(dictInfo.getDbConfigCode(), dictInfo.getDictCode());
// 數據字典
} else if (StringUtils.isNotEmpty(dictInfo.getDataDictCode())) {
dictionaryInfos=dataDictResultFactory.getResult(dictInfo.getDataDictCode());
}
dictMap.put(key, dictionaryInfos);
}
});
functionInfo.setDictDatas(dictMap);
}
}
至此,后端功能業已完成。此下,將進行改造前端功能。
/**
* 獲取字典數據
*
* @param elementConfig
* @returns {null|*}
*/
engine.getDictData=function (elementConfig) {
if (self.funcMessage) {
let key=[elementConfig['dictCode'], elementConfig['dataDictCode'], elementConfig['dictDBConfigCode']].join(
"");
return self.funcMessage.dictDatas[key];
}
return null;
}
此函數,內置在基礎引擎js段中,故可以直接獲取功能對象,并通過功能對象直接獲取字典數據。
此下,我選了選擇框元素進行改造,其他的復刻即可。
// 定義選擇框元素
let selectView=factory.byId("${fieldCode}");
// 定義過濾lay
let layFilter="${fieldCode}_" + self.containerId;
// 設置屬性
selectView.attr("lay-filter", layFilter);
// 獲取字典
let dicts=factory.getDictData(elementConfig);
if (!engineCommon.isListEmpty(dicts)) {
for (let i=0, length=dicts.length; i < length; i++) {
try {
let dict=dicts[i];
let dictionaryValue=dict["dictionaryValue"];
let optionHtml="<option name='" + layFilter + "' id='${fieldCode}_" + dictionaryValue + "' value='" + dictionaryValue + "'>" + dictionary["dictionaryName"] + "</option>";
selectView.append(optionHtml);
} catch(e) {
console.log(e);
}
}
layFactory.render('select');
}
// 監聽選中事件
layFactory.on(LayEventConstants.SELECT, layFilter, function(data){
selectView.val(data.value);
// 回調lay事件
if (self.factory && self.factory['onLayEvent']) {
self.factory.onLayEvent('${fieldCode}', LayEventConstants.SELECT, data);
}
});
至此,其他字典組件相繼修改,既完成。
在尋找支持在Java中用編程方法處理各類格式文檔的API嗎?好巧,Java版企業級文檔管理組合套包Spire.Office 2020全新上線!Word、Excel、PPT、PDF、條形碼等格式一網打盡。
目前,Spire.Office for Java 迎來v3.1.0版的更新。Excel格式處理控件Spire.XLS(點擊下載)強勢加入,同時各個產品也增加了新功能。如Spire.PDF 支持轉換PDF到SVG時設置寬度和高度像素,并公開了PdfFileLinkAnnotationWidget類及getValue屬性;Spire.Presentation 支持轉換PPT到HMTL時,可設置內容居中;同時,也修復了將PDF轉換為Word/HTML、PPT轉換為PDF/圖片、Word轉換為PDF/HTML、給PPT設置圖片背景、對Word文檔進行加密時出現的問題。
新功能及問題修復詳情,請參閱如下內容。(點擊文末“了解更多”可下載Spire.office全部產品)
新功能:
問題修復:
新功能:
問題修復:
問題修復:
述
在表單的設計過程中,會有一些表單字段需要在已知的內容中進行選擇,這在html中會使用select組件來設計該表單字段。而在Material中,同樣有與之對應的 <mat-select> 組件。這種組件在選項內容較少的情況下使用非常方便,能很好的引導用戶在表單中,輸入規范的內容:
但是在選項內容過多時,這種 <mat-select> 組件,對用戶來說,想要找到自己想要的選項就比較麻煩,需要一個個選項去對比,查找:
幸運的是,在Material中提供了 mat-autocomplete ,這個組件和我們以前在JQuery時代使用的select2類似。它支持用戶鍵盤輸入功能,并根據輸入內容在已有的數據集合中進行過濾,選項內容動態的顯示過濾后的結果。
html:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{option}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
TypeScript:
import {Component} from '@angular/core';import {FormControl} from '@angular/forms';/**
* @title Simple autocomplete
*/@Component({
selector: 'autocomplete-simple-example',
templateUrl: 'autocomplete-simple-example.html',
styleUrls: ['autocomplete-simple-example.css'],
})export class AutocompleteSimpleExample {
myControl=new FormControl();
options: string[]=['One', 'Two', 'Three'];
}
呈現的效果如下:
在簡單用法中,我們發現,選項內容為字符串,選擇中的值和顯示的值為相同的。但在實際的應用過程中,我們需要選中的值和顯示的內容是不同的。
舉個例子,我們有個需要選擇企業的字段,要存儲到數據庫的值是企業的編碼,而在界面上顯示的需要是企業的名稱。在這個例子中,我們有個企業的數據結構:
interface Company {
code: string;
name: string;
}
companies: Company[]=[]; constructor() { this.companies.push({code: 'qiye-01', name: '企業01'}); this.companies.push({code: 'qiye-02', name: '企業02'}); this.companies.push({code: 'qiye-03', name: '企業03'}); this.companies.push({code: 'qiye-04', name: '企業04'});
}
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
在html中,我們將mat-option的顯示內容修改成name,如 {{option.name}} 。這樣我們的下拉選項中就會顯示企業的名稱:
但是如果我們選中其中一個選項,就會發現結果和我們想象的不一樣,選中后的結果顯示并不是我們想要的企業名稱
此時,我們就需要使用API中提供的
在MatAutocomplete的API: displayWith。文檔已經很明確的告訴我們 displayWith 需要我們傳入一個已定義的函數
@Input()
displayWith: ((value: any)=> string) | null
其中的函數形參value指的是 <mat-option *ngFor="let option of companies" [value]="option"> 中value對應的option,此處option就是一個我們后臺定義的Company。這樣我們的value就擁有code和name兩個屬性。
這樣我們就可以定義一個displayWith的函數:
displayFn(value: any): string { // value: Company
return value ? value.name : undefined;
}
同時修改html
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option></mat-autocomplete>
這樣就會顯示我們想要的結果
在這種方式下,我們的formControl得到的其實是一個company對象,要存儲code,需要在從company對象中獲取code屬性值。
那么如果我們想在formControl中直接得到code呢?
擴展用法通過分析,我們發現,formControl獲得值的內容,其實和中的value是一致的。
因此,我們可以做如下修改, 將value改成option.code
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
得到如下顯示結果:
發現顯示的是code值,那我們還可以使用displayWith函數,讓它顯示企業名稱嗎?
答案是可以的。不過我們就不能再使用上面定義的displayFn方法了。此時我們需要重新定義一個更高級的方法:
displayWith() { return (value)=> { if (value) { const arr=that.companies.filter(item=> item.code===value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
}
在html中的使用方法如下:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayWith()">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
需要[displayWith]=”displayWithThis(this)” 這樣特殊的使用方法。
為什么同樣是用displayWith,不同的value中,實際寫法差異卻如此大呢?這個和JS的This作用于有關。
在不是用匿名函數的情況下,如:
displayFn(value) { if (value) { const arr=this.companies.filter(item=> item.code===value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
此時,如果將displayFn直接設置到mat-autocomplete中,此時的this表示mat-autocomplete對象,這樣this.companies.filter就會拋出異常。
在本文中,我們主要講了MatAutocomplete在實際開發過程中,在不同的業務場景中,同樣的組件我們用到的不同的實現方式。每一種語言都有它自己的特性,掌握了這些特性,會讓我們工作時更加得心應手。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。