主从(明细)

处理分层数据的最简单方法是主 - 细节模型。 使用控件选择主项目和一个或多个其他控件以显示主项目的详细信息。

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjCore from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.input'; import * as wjGrid from '@grapecity/wijmo.grid'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // // create some random data var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','); var products = 'Phones,Cars,Stereos,Watches,Computers'.split(','); var data = []; for (var i = 0; i < 50; i++) { data.push({ id: i, country: countries[i % countries.length], product: products[i % products.length], date: wjCore.DateTime.addDays(new Date(), i), sales: Math.random() * 10000, expenses: Math.random() * 5000, }); } // // show countries in combo var theCombo = new wjInput.ComboBox('#theCombo', { itemsSource: countries, selectedIndexChanged: function () { view.refresh(); // refresh view to filter by country } }); // // create CollectionView to show items for the selected country var view = new wjCore.CollectionView(data, { filter: function (item) { return item.country == theCombo.text; } }); // // show items for the selected country in the detail grid var theGridDetail = new wjGrid.FlexGrid('#theGridDetail', { itemsSource: view }); // // using a grid as the master var theGridMaster = new wjGrid.FlexGrid('#theGridMaster', { itemsSource: data, selectionMode: 'Row', isReadOnly: true, selectionChanged: function (s, e) { updateDetailControls(); } }); // // update detail controls when selection changes function updateDetailControls() { var item = theGridMaster.collectionView.currentItem; var bndCtls = document.querySelectorAll('.bnd-ctl'); for (var i = 0; i < bndCtls.length; i++) { var host = bndCtls[i]; var prop = host.id.substr(3).toLowerCase(); var ctl = wjCore.Control.getControl(host); if (wjCore.isString(item[prop])) { ctl['text'] = item[prop]; } else { ctl['value'] = item[prop]; } } } // // set a property on the current item function setProperty(prop, val) { var v = theGridMaster.collectionView; v.editItem(v.currentItem); v.currentItem[prop] = val; v.commitEdit(); } // // define detail controls var theCountry = new wjInput.ComboBox('#theCountry', { itemsSource: countries, textChanged: function (s, e) { setProperty('country', s.text); } }); var theProduct = new wjInput.ComboBox('#theProduct', { itemsSource: products, textChanged: function (s, e) { setProperty('product', s.text); } }); var theDate = new wjInput.InputDate('#theDate', { valueChanged: function (s, e) { setProperty('date', s.value); } }); var theSales = new wjInput.InputNumber('#theSales', { format: 'n2', step: 10, valueChanged: function (s, e) { setProperty('sales', s.value); } }); var theExpenses = new wjInput.InputNumber('#theExpenses', { format: 'n2', step: 10, valueChanged: function (s, e) { setProperty('expenses', s.value); } }); } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Master-Detail Separate Grids</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <p> For example, the ComboBox below is used as a master control. Select a country from the combo and the grid below will show the items in that country:</p> <label> Select a country: <div id="theCombo"></div> </label> <div id="theGridDetail"></div> <p> In the next example, The FlexGrid is used as the master control. Select an item on the grid and see the details in the controls below:</p> <h3> Master</h3> <div id="theGridMaster"></div> <h3> Detail</h3> <table class="tbl-spaced"> <tr> <td>Country:</td> <td id="theCountry" class="bnd-ctl"></td> </tr> <tr> <td>Product:</td> <td id="theProduct" class="bnd-ctl"></td> </tr> <tr> <td>Date:</td> <td id="theDate" class="bnd-ctl"></td> </tr> <tr> <td>Sales:</td> <td id="theSales" class="bnd-ctl"></td> </tr> <tr> <td>Expenses:</td> <td id="theExpenses" class="bnd-ctl"></td> </tr> </table> <p></p> <div class="panel panel-warning"> <div class="panel-heading"> This example requires a fair amount of code because we are not using any frameworks. With Angular, React, or Vue, no code would be required to update the child controls. </div> </div> </div> </body> </html> .wj-flexgrid { max-height: 220px; margin: 6px 0px; } .tbl-spaced td { vertical-align: middle; margin: 3px; } body { margin-bottom: 24pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wjCore from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.input'; import { Component, enableProdMode, NgModule, ViewChild, AfterViewInit } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import * as wjGrid from '@grapecity/wijmo.grid'; import { WjGridModule } from '@grapecity/wijmo.angular2.grid'; import { WjInputModule } from '@grapecity/wijmo.angular2.input'; class DataItem { id: number; country: string; product: string; date: Date; sales: number; expenses: number; } // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent implements AfterViewInit { @ViewChild('countriesCombo') countriesCombo: wjInput.ComboBox; @ViewChild('theGridMaster') theGridMaster: wjGrid.FlexGrid; countries: string[]; products: string[]; data: DataItem[]; detailView: wjCore.CollectionView; // DataSvc will be passed by derived classes constructor() { this.countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','); this.products = 'Phones,Cars,Stereos,Watches,Computers'.split(','); this.data = this._getData(); } ngAfterViewInit() { var self = this; self.detailView = new wjCore.CollectionView(this.data, { filter: function (item: DataItem) { return item.country === self.countriesCombo.text; } }) } onCountriesSelectedIndexChanged() { this.detailView.refresh(); } onSelectionChanged() { var item = this.theGridMaster.collectionView.currentItem; var bndCtls = document.querySelectorAll('.bnd-ctl'); for (var i = 0; i < bndCtls.length; i++) { var host = bndCtls[i]; var prop = host.id.substr(3).toLowerCase(); var ctl = wjCore.Control.getControl(host); if (wjCore.isString(item[prop])) { ctl['text'] = item[prop]; } else { ctl['value'] = item[prop]; } } } setProperty(prop: string, val: any) { var v = <wjCore.CollectionView>this.theGridMaster.collectionView; v.editItem(v.currentItem); v.currentItem[prop] = val; v.commitEdit(); } private _getData(): DataItem[] { var data = []; for (var i = 0; i < 50; i++) { data.push({ id: i, country: this.countries[i % this.countries.length], product: this.products[i % this.products.length], date: wjCore.DateTime.addDays(new Date(), i), sales: Math.random() * 10000, expenses: Math.random() * 5000, }); } return data; } } // @NgModule({ imports: [WjGridModule, WjInputModule, BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Master-Detail Separate Grids</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <p> For example, the ComboBox below is used as a master control. Select a country from the combo and the grid below will show the items in that country:</p> <label> Select a country: <wj-combo-box #countriesCombo [itemsSource]="countries" (selectedIndexChanged)="onCountriesSelectedIndexChanged()"> </wj-combo-box> </label> <wj-flex-grid [itemsSource]="detailView"></wj-flex-grid> <p> In the next example, The FlexGrid is used as the master control. Select an item on the grid and see the details in the controls below:</p> <h3>Master</h3> <wj-flex-grid #theGridMaster [itemsSource]="data" [selectionMode]="'Row'" [isReadOnly]=true (selectionChanged)="onSelectionChanged()"></wj-flex-grid> <h3>Detail</h3> <table class="tbl-spaced"> <tr> <td>Country:</td> <td> <wj-combo-box #theCountry [itemsSource]="countries" class="bnd-ctl" id="theCountry" (textChanged)="setProperty('country', theCountry.text)"></wj-combo-box> </td> </tr> <tr> <td>Product:</td> <wj-combo-box #theProduct [itemsSource]="products" class="bnd-ctl" id="theProduct" (textChanged)="setProperty('product', theProduct.text)"></wj-combo-box> </tr> <tr> <td>Date:</td> <wj-input-date #theDate class="bnd-ctl" id="theDate" (valueChanged)="setProperty('date', theDate.value)"> </wj-input-date> </tr> <tr> <td>Sales:</td> <wj-input-number #theSales class="bnd-ctl" id="theSales" [format]="'n2'" [step]=10 (valueChanged)="setProperty('sales', theSales.value)"> </wj-input-number> </tr> <tr> <td>Expenses:</td> <wj-input-number #theExpenses class="bnd-ctl" id="theExpenses" [format]="'n2'" [step]=10 (valueChanged)="setProperty('expenses', theExpenses.value)"> </wj-input-number> </tr> </table> <p></p> </div> .wj-flexgrid { max-height: 220px; margin: 6px 0px; } .tbl-spaced td { vertical-align: middle; margin: 3px; } body { margin-bottom: 24pt; } <template> <div class="container-fluid"> <p> For example, the ComboBox below is used as a master control. Select a country from the combo and the grid below will show the items in that country: </p> <label>Select a country: <wj-combo-box :initialized="initCountriesCombo" :itemsSource="countries" :selectedIndexChanged="onCountriesSelectedIndexChanged" ></wj-combo-box> </label> <wj-flex-grid :itemsSource="detailView"></wj-flex-grid> <p> In the next example, The FlexGrid is used as the master control. Select an item on the grid and see the details in the controls below: </p> <h3>Master</h3> <wj-flex-grid :initialized="initGridMaster" :itemsSource="gridData" :selectionMode="'Row'" :isReadOnly="true" :selectionChanged="onSelectionChanged" ></wj-flex-grid> <h3>Detail</h3> <table class="tbl-spaced"> <tr> <td>Country:</td> <td> <wj-combo-box :itemsSource="countries" class="bnd-ctl" id="theCountry" :textChanged="this.setProperty.bind(this,'country')" ></wj-combo-box> </td> </tr> <tr> <td>Product:</td> <wj-combo-box :itemsSource="products" class="bnd-ctl" id="theProduct" :textChanged="this.setProperty.bind(this,'product')" ></wj-combo-box> </tr> <tr> <td>Date:</td> <wj-input-date class="bnd-ctl" id="theDate" :valueChanged="this.setProperty.bind(this,'date')" ></wj-input-date> </tr> <tr> <td>Sales:</td> <wj-input-number class="bnd-ctl" id="theSales" :format="'n2'" :step="10" :valueChanged="this.setProperty.bind(this,'sales')" ></wj-input-number> </tr> <tr> <td>Expenses:</td> <wj-input-number class="bnd-ctl" id="theExpenses" :format="'n2'" :step="10" :valueChanged="this.setProperty.bind(this,'expenses')" ></wj-input-number> </tr> </table> <p></p> </div> </template> <script> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import Vue from "vue"; import * as wjCore from "@grapecity/wijmo"; import * as wjcGrid from "@grapecity/wijmo.grid"; import * as wjInput from "@grapecity/wijmo.input"; import "@grapecity/wijmo.vue2.input"; import "@grapecity/wijmo.vue2.grid"; let App = Vue.extend({ name: "app", data: function() { return { countries: "US,Germany,UK,Japan,Italy,Greece".split(","), products: "Phones,Cars,Stereos,Watches,Computers".split(","), gridData: null, detailView: null }; }, methods: { onCountriesSelectedIndexChanged: function() { this.detailView.refresh(); }, onSelectionChanged: function() { var item = this.theGridMaster.collectionView.currentItem; var bndCtls = document.querySelectorAll(".bnd-ctl"); for (var i = 0; i < bndCtls.length; i++) { var host = bndCtls[i]; var prop = host.id.substr(3).toLowerCase(); var ctl = wjCore.Control.getControl(host); if (wjCore.isString(item[prop])) { ctl["text"] = item[prop]; } else { ctl["value"] = item[prop]; } } }, setProperty: function(prop, sender) { var val = prop === "country" || prop === "product" ? sender.text : sender.value; var v = this.theGridMaster.collectionView; v.editItem(v.currentItem); v.currentItem[prop] = val; v.commitEdit(); }, getData: function() { var data = []; for (var i = 0; i < 50; i++) { data.push({ id: i, country: this.countries[i % this.countries.length], product: this.products[i % this.products.length], date: wjCore.DateTime.addDays(new Date(), i), sales: Math.random() * 10000, expenses: Math.random() * 5000 }); } return data; }, initGridMaster: function(theGridMaster) { this.theGridMaster = theGridMaster; }, initCountriesCombo: function(countriesCombo) { this.countriesCombo = countriesCombo; } }, mounted: function() { this.gridData = this.getData(); var self = this; this.detailView = new wjCore.CollectionView(this.gridData, { filter: function(item) { return item.country === self.countriesCombo.text; } }); } }); new Vue({ render: h => h(App) }).$mount("#app"); </script> <style> .wj-flexgrid { max-height: 220px; margin: 6px 0px; } .tbl-spaced td { vertical-align: middle; margin: 3px; } body { margin-bottom: 24pt; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AutoComplete</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"> </div> </body> </html> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import "./app.css"; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjCore from "@grapecity/wijmo"; import * as wjcGrid from "@grapecity/wijmo.react.grid"; import * as wjInput from "@grapecity/wijmo.react.input"; class App extends React.Component { constructor(props) { super(props); this.state = { countries: "US,Germany,UK,Japan,Italy,Greece".split(","), products: "Phones,Cars,Stereos,Watches,Computers".split(","), gridData: [], detailView: null }; } componentDidMount() { this.setState({ gridData: this.getData(), detailView: new wjCore.CollectionView(this.getData(), { filter: (item) => { return item.country === this.countriesCombo.text; } }) }); } render() { return <div className="container-fluid"> <p> For example, the ComboBox below is used as a master control. Select a country from the combo and the grid below will show the items in that country: </p> <label>Select a country: <wjInput.ComboBox initialized={this.initCountriesCombo.bind(this)} itemsSource={this.state.countries} selectedIndexChanged={this.onCountriesSelectedIndexChanged.bind(this)}> </wjInput.ComboBox> </label> <wjcGrid.FlexGrid itemsSource={this.state.detailView}> </wjcGrid.FlexGrid> <p> In the next example, The FlexGrid is used as the master control. Select an item on the grid and see the details in the controls below: </p> <h3>Master</h3> <wjcGrid.FlexGrid initialized={this.initGridMaster.bind(this)} itemsSource={this.state.gridData} selectionMode={'Row'} isReadOnly={true} selectionChanged={this.onSelectionChanged.bind(this)}> </wjcGrid.FlexGrid> <h3>Detail</h3> <table className="tbl-spaced"> <tr> <td>Country:</td> <td> <wjInput.ComboBox itemsSource={this.state.countries} className="bnd-ctl" id="theCountry" textChanged={this.setProperty.bind(this, 'country')}> </wjInput.ComboBox> </td> </tr> <tr> <td>Product:</td> <td> <wjInput.ComboBox itemsSource={this.state.products} className="bnd-ctl" id="theProduct" textChanged={this.setProperty.bind(this, 'product')}> </wjInput.ComboBox> </td> </tr> <tr> <td>Date:</td> <td> <wjInput.InputDate className="bnd-ctl" id="theDate" textChanged={this.setProperty.bind(this, 'date')}> </wjInput.InputDate> </td> </tr> <tr> <td>Sales:</td> <wjInput.InputNumber className="bnd-ctl" id="theSales" format="'n2'" step={10} valueChanged={this.setProperty.bind(this, 'sales')}></wjInput.InputNumber> </tr> <tr> <td>Expenses:</td> <wjInput.InputNumber className="bnd-ctl" id="theExpenses" format="'n2'" step={10} valueChanged={this.setProperty.bind(this, 'expenses')}></wjInput.InputNumber> </tr> </table> </div>; } initCountriesCombo(countriesCombo) { this.countriesCombo = countriesCombo; } onCountriesSelectedIndexChanged() { this.state.detailView.refresh(); } initGridMaster(theGridMaster) { this.theGridMaster = theGridMaster; } onSelectionChanged() { var item = this.theGridMaster.collectionView.currentItem; var bndCtls = document.querySelectorAll(".bnd-ctl"); for (var i = 0; i < bndCtls.length; i++) { var host = bndCtls[i]; var prop = host.id.substr(3).toLowerCase(); var ctl = wjCore.Control.getControl(host); if (wjCore.isString(item[prop])) { ctl["text"] = item[prop]; } else { ctl["value"] = item[prop]; } } } setProperty(prop, sender) { var val = prop === "country" || prop === "product" ? sender.text : sender.value; var v = this.theGridMaster.collectionView; v.editItem(v.currentItem); v.currentItem[prop] = val; v.commitEdit(); } getData() { var data = []; for (var i = 0; i < 50; i++) { data.push({ id: i, country: this.state.countries[i % this.state.countries.length], product: this.state.products[i % this.state.products.length], date: wjCore.DateTime.addDays(new Date(), i), sales: Math.random() * 10000, expenses: Math.random() * 5000 }); } return data; } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Grapecity Wijmo OLAP Pivot Chart Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> .wj-flexgrid { max-height: 220px; margin: 6px 0px; } .tbl-spaced td { vertical-align: middle; margin: 3px; } body { margin-bottom: 24pt; }