摘要:如今已經實現了該控件,本文將主要介紹如何創建自定義控件以及其源代碼。我們需要將目光聚焦在函數的邏輯中,該函數使用這些屬性,并在其值或選擇更改時自動調用以更新控件頭構造函數到此為止,我們幾乎已經完成了控件架構。
概述
最近,有客戶向我們請求開發一個前端下拉控件,需求是顯示了一個列表,其中包含可由用戶多帶帶選擇的項目控件,該控件將在下拉列表中顯示多選TreeView(樹形圖)。
如今WijmoJS已經實現了該控件——DropDownTree,本文將主要介紹如何創建自定義DropDownTree控件以及其源代碼。
DropDownTree 控件源代碼HTML
DropDownTree Control
The DropDownTree control is similar to a MultiSelect, but it hosts a TreeView in the drop-down instead of a ListBox.
The DropDownTree"s object model is also similar to the MultiSelect"s: you can listen to the checkedItemsChanged event and get/set the selection using the checkedItems property:
Drop-Down-Tree
Multi-Select
JavaScript
onload = function() { // create the DropDownTree var ddTree = new wijmo.input.DropDownTree("#ddTree", { displayMemberPath: "header", childItemsPath: "items", showCheckboxes: true, itemsSource: getTreeData(), checkedItemsChanged: function (s, e) { console.log("dropDownTree.checkedItemsChanged:"); s.checkedItems.forEach(function (item, index) { console.log(index, item[s.displayMemberPath]) }) } }); // create the MultiSelect var multiSelect = new wijmo.input.MultiSelect("#multiSelect", { itemsSource: "Austria,Belgium,Chile,Denmark".split(","), checkedItemsChanged: function (s, e) { console.log("multiSelect.checkedItemsChanged:"); s.checkedItems.forEach(function (item, index) { console.log(index, item) }) } }); // get the tree data function getTreeData() { return [ { header: "Electronics", img: "resources/electronics.png", items: [ { header: "Trimmers/Shavers" }, { header: "Tablets" }, { header: "Phones", img: "resources/phones.png", items: [ { header: "Apple" }, { header: "Motorola", newItem: true }, { header: "Nokia" }, { header: "Samsung" } ]}, { header: "Speakers", newItem: true }, { header: "Monitors" } ]}, { header: "Toys", img: "resources/toys.png", items: [ { header: "Shopkins" }, { header: "Train Sets" }, { header: "Science Kit", newItem: true }, { header: "Play-Doh" }, { header: "Crayola" } ]}, { header: "Home", img: "resources/home.png", items: [ { header: "Coffeee Maker" }, { header: "Breadmaker", newItem: true }, { header: "Solar Panel", newItem: true }, { header: "Work Table" }, { header: "Propane Grill" } ]} ]; } } // DropDownTree: transpiled TypeScript var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var wijmo; (function (wijmo) { var input; (function (input) { var DropDownTree = /** @class */ (function (_super) { __extends(DropDownTree, _super); /** * Initializes a new instance of the @see:DropDownTree class. * * @param element The DOM element that hosts the control, or a CSS selector for the host element (e.g. "#theCtrl"). * @param options The JavaScript object containing initialization data for the control. */ function DropDownTree(element, options) { var _this = _super.call(this, element) || this; _this._maxHdrItems = 2; _this._hdrFmt = wijmo.culture.MultiSelect.itemsSelected; /** * Occurs when the value of the @see:checkedItems property changes. */ _this.checkedItemsChanged = new wijmo.Event(); wijmo.addClass(_this.hostElement, "wj-dropdowntree"); // make header element read-only _this._tbx.readOnly = true; // toggle drop-down when clicking on the header element // (and not on a containing label element) _this.addEventListener(_this.inputElement, "click", function (e) { if (document.elementFromPoint(e.clientX, e.clientY) == _this.inputElement) { _this.isDroppedDown = !_this.isDroppedDown; } }); // update header now, when the itemsSource changes, and when items are selected _this._updateHeader(); var tree = _this._tree; tree.checkedItemsChanged.addHandler(function () { _this._updateHeader(); _this.onCheckedItemsChanged(); }); tree.selectedItemChanged.addHandler(function () { if (!tree.showCheckboxes) { _this._updateHeader(); _this.onCheckedItemsChanged(); } }); tree.loadedItems.addHandler(function () { _this._updateHeader(); }); // close tree on enter/escape tree.addEventListener(tree.hostElement, "keydown", function (e) { switch (e.keyCode) { case wijmo.Key.Enter: case wijmo.Key.Escape: _this.isDroppedDown = false; break; } }); // initialize control options _this.initialize(options); return _this; } Object.defineProperty(DropDownTree.prototype, "treeView", { /** * Gets the @see:TreeView control shown in the drop-down. */ get: function () { return this._tree; }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "itemsSource", { /** * Gets or sets the array that contains the @see:TreeView items. * * @see:TreeView #see:itemsSource arrays usually have a hierarchical * structure with items that contain child items. There is no fixed * limit to the depth of the items. * * For details, see the @see:TreeView.itemsSource property. */ get: function () { return this._tree.itemsSource; }, set: function (value) { this._tree.itemsSource = value; }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "displayMemberPath", { /** * Gets or sets the name of the property (or properties) to use as * the visual representation of the nodes. * * The default value for this property is the string "header". * * For details, see the @see:TreeView.displayMemberPath property. */ get: function () { return this._tree.displayMemberPath; }, set: function (value) { this._tree.displayMemberPath = value; }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "childItemsPath", { /** * Gets or sets the name of the property (or properties) that contains * the child items for each node. * * The default value for this property is the string "items". * * For details, see the @see:TreeView.childItemsPath property. */ get: function () { return this._tree.childItemsPath; }, set: function (value) { this._tree.childItemsPath = value; }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "showCheckboxes", { /** * Gets or sets a value that determines whether the @see:TreeView should * add checkboxes to nodes and manage their state. * * For details, see the @see:TreeView.showCheckboxes property. */ get: function () { return this._tree.showCheckboxes; }, set: function (value) { this._tree.showCheckboxes = value; }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "checkedItems", { /** * Gets or sets an array containing the items that are currently checked. */ get: function () { var tree = this._tree; if (tree.showCheckboxes) { return tree.checkedItems; } else { return tree.selectedItem ? [tree.selectedItem] : []; } }, set: function (value) { var tree = this._tree; if (tree.showCheckboxes) { tree.checkedItems = wijmo.asArray(value); } else { tree.selectedItem = wijmo.isArray(value) ? value[0] : value; } }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "maxHeaderItems", { /** * Gets or sets the maximum number of items to display on the control header. * * If no items are selected, the header displays the text specified by the * @see:placeholder property. * * If the number of selected items is smaller than or equal to the value of the * @see:maxHeaderItems property, the selected items are shown in the header. * * If the number of selected items is greater than @see:maxHeaderItems, the * header displays the selected item count instead. */ get: function () { return this._maxHdrItems; }, set: function (value) { if (this._maxHdrItems != value) { this._maxHdrItems = wijmo.asNumber(value); this._updateHeader(); } }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "headerFormat", { /** * Gets or sets the format string used to create the header content * when the control has more than @see:maxHeaderItems items checked. * * The format string may contain the "{count}" replacement string * which gets replaced with the number of items currently checked. * The default value for this property in the English culture is * "{count:n0} items selected". */ get: function () { return this._hdrFmt; }, set: function (value) { if (value != this._hdrFmt) { this._hdrFmt = wijmo.asString(value); this._updateHeader(); } }, enumerable: true, configurable: true }); Object.defineProperty(DropDownTree.prototype, "headerFormatter", { /** * Gets or sets a function that gets the HTML in the control header. * * By default, the control header content is determined based on the * @see:placeholder, @see:maxHeaderItems, and on the current selection. * * You may customize the header content by specifying a function that * returns a custom string based on whatever criteria your application * requires. */ get: function () { return this._hdrFormatter; }, set: function (value) { if (value != this._hdrFormatter) { this._hdrFormatter = wijmo.asFunction(value); this._updateHeader(); } }, enumerable: true, configurable: true }); /** * Raises the @see:checkedItemsChanged event. */ DropDownTree.prototype.onCheckedItemsChanged = function (e) { this.checkedItemsChanged.raise(this, e); }; //** overrides // switch focus to the tree when the drop-down opens DropDownTree.prototype.onIsDroppedDownChanged = function (e) { if (this.containsFocus() && this.isDroppedDown) { this._tree.focus(); } _super.prototype.onIsDroppedDownChanged.call(this, e); }; // create the drop-down element DropDownTree.prototype._createDropDown = function () { // create child TreeView control var lbHost = wijmo.createElement("", this._dropDown); this._tree = new wijmo.nav.TreeView(lbHost, { showCheckboxes: true, }); // let base class do its thing _super.prototype._createDropDown.call(this); }; Object.defineProperty(DropDownTree.prototype, "isReadOnly", { // override since our input is always read-only get: function () { return this._readOnly; }, set: function (value) { this._readOnly = wijmo.asBoolean(value); wijmo.toggleClass(this.hostElement, "wj-state-readonly", this.isReadOnly); }, enumerable: true, configurable: true }); // update header when refreshing DropDownTree.prototype.refresh = function (fullUpdate) { if (fullUpdate === void 0) { fullUpdate = true; } _super.prototype.refresh.call(this, fullUpdate); this._updateHeader(); }; //** implementation // update the value of the control header DropDownTree.prototype._updateHeader = function () { // get selected items var items = this.checkedItems; // update the header if (wijmo.isFunction(this._hdrFormatter)) { this.inputElement.value = this._hdrFormatter(); } else { var hdr = ""; if (items.length > 0) { if (items.length <= this._maxHdrItems) { if (this.displayMemberPath) { var binding_1 = new wijmo.Binding(this.displayMemberPath); items = items.map(function (item) { return binding_1.getValue(item); }); } hdr = items.join(", "); } else { hdr = wijmo.format(this.headerFormat, { count: items.length }); } } this.inputElement.value = hdr; } // update wj-state attributes this._updateState(); }; return DropDownTree; }(input.DropDown)); input.DropDownTree = DropDownTree; })(input = wijmo.input || (wijmo.input = {})); })(wijmo || (wijmo = {})); //# sourceMappingURL=DropDownTree.js.map
CSS
body { margin-bottom: 24pt; }
控件準備就緒后,它將如下所示:
本控件使用兩個獨立的WijmoJS模塊:輸入和導航。所需的步驟與開發MultiSelect控件時所采用的步驟相同:
選擇基類在這種場景下,我們可以將DropDown控件進行擴展,該控件包含使用下拉按鈕實現輸入元素所需的所有邏輯以及可用于托管任何控件的通用下拉列表。 DropDown控件用作ComboBox,InputColor和InputDate控件的基類。
定義對象模型因為DropDownTree控件在其下拉列表中托管TreeView,所以我們決定直接從DropDownTree公開TreeView控件的主要屬性:
TreeView獲取對下拉列表中顯示的TreeView控件的引用。
ItemsSource獲取或設置對用于填充TreeView的對象數組的引用。
DisplayMemberPath獲取或設置用作項目可視化表示的屬性名稱(默認為“header”)。
ChildItemsPath獲取或設置包含數據源中每個項的子項的屬性的名稱(默認為“items”)。
ShowCheckboxes獲取或設置一個值,該值確定控件是否應為每個項添加復選框,以便用戶可以選擇多個項(默認為true)。
我們還添加了一些額外的屬性和事件來定義當前選擇以及它在控制頭中的表示方式。這些屬性鏡像MultiSelect控件中的相應屬性:
CheckedItems獲取或設置包含當前所選項目的數組。
CheckedItemsChanged是CheckedItems屬性值更改時發生的事件。
MaxHeaderItems是控件頭中顯示的最大選定項數。
當控件具有超過*maxHeaderItems項目選項時,headerFormat獲取或設置用于創建標題內容的格式字符串。
HeaderFormatter獲取或設置一個函數,該函數獲取控件頭中顯示的文本。 這將覆蓋maxHeaderItems和headerFormat屬性的設置。
實現控件我們首先將控件聲明為基類的擴展:
namespace wijmo.input { export class DropDownTree extends DropDown { } }
“extendsDropDown”語句確保我們的控件繼承基本DropDown類的所有功能,包括屬性,事件,方法和所有內部/私有成員。
創建樹視圖接下來,我們覆蓋DropDown類中的_createDropDown方法,以創建將在下拉列表中顯示的TreeView控件。
除了創建TreeView之外,我們還會覆蓋onIsDroppedDownChanged方法,以便在下拉列表打開且控件具有焦點時將焦點轉移到樹。 這允許用戶使用鍵盤導航樹。 他們可以通過鍵入內容來搜索項目,通過按空格鍵來檢查項目,或使用光標鍵導航樹。
namespace wijmo.input { export class DropDownTree extends DropDown { private _tree: wijmo.nav.TreeView; // create the drop-down element protected _createDropDown() { // create child TreeView control let lbHost = document.createElement("div"); setCss(lbHost, { width: "100%", border: "none" }); this._tree = new wijmo.nav.TreeView(lbHost, { showCheckboxes: true }); } // switch focus to the tree when the drop-down opens onIsDroppedDownChanged(e?: EventArgs) { if (this.containsFocus() && this.isDroppedDown) { this._tree.focus(); } super.onIsDroppedDownChanged(e); } } }公開TreeView及其屬性
下一步是添加公開TreeView及其主要屬性:
namespace wijmo.input { export class DropDownTree extends DropDown { private _tree: wijmo.nav.TreeView; get treeView(): wijmo.nav.TreeView { return this._tree; } get itemsSource(): any[] { return this._tree.itemsSource; } set itemsSource(value: any[]) { this._tree.itemsSource = value; } // same for displayMemberPath, childItemsPath, // and showCheckboxes // create the drop-down element protected _createDropDown() {…} } }
這些屬性只是獲取或設置包含的TreeView上的相應屬性的快捷方式。 因此,它們非常簡單,我們甚至不啟用類型檢查,因為TreeView將為我們處理。
CheckedItems屬性控件的主要屬性是CheckedItems,它用來表示用戶當前已獲取和自定義的數組。 我們可以用它實現上面那樣的傳遞屬性,也可以實現多選和單選功能。比如想實現其單選功能時,我們需要檢查ShowCheckboxes屬性的值并使用樹的checkedItems或selectedItem屬性。
除了CheckedItems屬性,我們還實現了checkedItemsChanged事件及其伴隨方法onCheckedItemsChanged。 這是WijmoJS事件的標準模式。 每個事件X都有一個相應的onX方法,負責觸發事件。
namespace wijmo.input { export class DropDownTree extends DropDown { private _tree: wijmo.nav.TreeView; // TreeView pass-through properties… get checkedItems(): any[] { let tree = this._tree; if (tree.showCheckboxes) { return tree.checkedItems; } else { return tree.selectedItem ? [tree.selectedItem] : []; } } set checkedItems(value: any[]) { let tree = this._tree; if (tree.showCheckboxes) { tree.checkedItems = asArray(value); } else { tree.selectedItem = isArray(value) ? value[0] : value; } } readonly checkedItemsChanged = new Event(); onCheckedItemsChanged(e?: EventArgs) { this.checkedItemsChanged.raise(this, e); } // create the drop-down element protected _createDropDown() {…} }
請注意,即使在單個選擇的情況下,checkedItems屬性也會返回一個數組(該數組為空或包含單個元素)。
更新控件頭這里不會重點討論maxHeaderItems,headerFormat或headerFormatter屬性的實現方式,因為它們很簡單。我們需要將目光聚焦在_updateHeader函數的邏輯中,該函數使用這些屬性,并在其值或選擇更改時自動調用以更新控件頭:
namespace wijmo.input { export class DropDownTree extends DropDown { private _tree: wijmo.nav.TreeView; // TreeView pass-through properties… // checketItems property… private _updateHeader() { let items = this.checkedItems; if (isFunction(this._hdrFormatter)) { this.inputElement.value = this._hdrFormatter(); } else { let hdr = ""; if (items.length > 0) { if (items.length <= this._maxHdrItems) { if (this.displayMemberPath) { let dmp = this.displayMemberPath, binding = new Binding(dmp); items = items.map((item) => { return binding.getValue(item); }); } hdr = items.join(", "); } else { hdr = format(this.headerFormat, { count: items.length }); } } this.inputElement.value = hdr; } } // create the drop-down element protected _createDropDown() {…} } }構造函數
到此為止,我們幾乎已經完成了控件架構。最后一步是實現構造函數,該構造函數將部件與事件偵聽器連接,并調用initialize方法以使用options參數中的用戶提供的值初始化屬性和事件處理程序:
namespace wijmo.input { export class DropDownTree extends DropDown { private _tree: wijmo.nav.TreeView; private _readOnly: boolean; private _maxHdrItems = 2; private _hdrFmt = wijmo.culture.MultiSelect.itemsSelected; private _hdrFormatter: Function; constructor(element: HTMLElement, options?: any) { super(element); addClass(this.hostElement, "wj-dropdowntree"); // make header element read-only this._tbx.readOnly = true; // update header now, when the itemsSource changes, // and when items are selected this._updateHeader(); let tree = this._tree; tree.checkedItemsChanged.addHandler(() => { this._updateHeader(); this.onCheckedItemsChanged(); }); tree.selectedItemChanged.addHandler(() => { if (!tree.showCheckboxes) { this._updateHeader(); this.onCheckedItemsChanged(); } }); tree.loadedItems.addHandler(() => { this._updateHeader(); }); // initialize control options this.initialize(options); } // TreeView pass-through properties… // checketItems property… // _updateHeader implementation… // _createDropDown implementation… } }測試控件
現在控件已準備好,我們可以測試它,并檢查它是否按照我們想要的方式運行。
運行DropDownTree 控件源代碼,單擊下拉按鈕以打開TreeView。 打開后,單擊幾個項目以選擇它們,并注意控件頭的更新方式:
我們由衷希望DropDownTree控件對您產生幫助。更重要的是,我們希望您現在可以放心地將DropDown控件擴展為托管其他類型的元素,同時創建自己的自定義控件。
WijmoJS:靈活高效的前端開發工具包,可快速搭建企業 Web 應用程序
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98921.html
摘要:年末促銷葡萄城歲末福利火熱放送中前端開發工具包年度第三個大版本已經正式發布,本次更新除了全面支持之外,還允許用戶使用在前端更高效地導出智能的分組表頭屬性全新的主題示例以及功能增強。 轉載請注明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 【年末促銷】葡萄城 2018 歲末福利火熱放送中 WijmoJS(前端開發工具包)2018年度第三個大版本已經正...
摘要:相反,我們將專注于將添加到用編寫的簡單應用程序中。使用創建應用程序。示例應用程序有兩個組件應用程序和。除在全球率先支持外,現已全面應用于等主流框架中。 showImg(https://segmentfault.com/img/bVbcvaQ?w=500&h=278); 概述 在本文中,我們將展示如何將WijmoJS與NPM和Webpack一起使用來創建最流行的基于JavaScript應...
摘要:全球最大的控件提供商葡萄城宣布,新一代純前端控件發布版本,進一步增強產品功能,并支持在上的安裝和發布,極大的提升了產品的易用性。葡萄城的控件和軟件產品在國內外屢獲殊榮,在全球被數十萬家企業學校和政府機構廣泛應用。 全球最大的控件提供商葡萄城宣布,新一代純前端控件 WijmoJS 發布2018 v1 版本,進一步增強產品功能,并支持在 Npm 上的安裝和發布,極大的提升了產品的易用性。 ...
閱讀 2434·2021-11-15 11:36
閱讀 1172·2019-08-30 15:56
閱讀 2243·2019-08-30 15:53
閱讀 1038·2019-08-30 15:44
閱讀 649·2019-08-30 14:13
閱讀 997·2019-08-30 10:58
閱讀 476·2019-08-29 15:35
閱讀 1293·2019-08-29 13:58