-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: remove Remote Example & replace with Import from CSV
- Loading branch information
1 parent
341a1a8
commit c998507
Showing
5 changed files
with
131 additions
and
325 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,53 @@ | ||
<h2> | ||
${title} | ||
<span class="float-end"> | ||
<a style="font-size: 18px" | ||
target="_blank" | ||
<div class="container-fluid"> | ||
<h2> | ||
Example 17: Dynamically Create Grid from CSV / Excel import | ||
<span class="float-end"> | ||
<a style="font-size: 18px" target="_blank" | ||
href="https://github.com/ghiscoding/aurelia-slickgrid/blob/master/packages/demo/src/examples/slickgrid/example17.ts"> | ||
<span class="mdi mdi-link-variant"></span> code | ||
</a> | ||
</span> | ||
</h2> | ||
<div class="subtitle" | ||
innerhtml.bind="subTitle"></div> | ||
<span class="mdi mdi-link-variant"></span> code | ||
</a> | ||
</span> | ||
<button | ||
class="ms-2 btn btn-outline-secondary btn-sm btn-icon" | ||
type="button" | ||
data-test="toggle-subtitle" | ||
click.trigger="toggleSubTitle()" | ||
> | ||
<span class="mdi mdi-information-outline" title="Toggle example sub-title details"></span> | ||
</button> | ||
</h2> | ||
|
||
<hr> | ||
<div if.bind="showSubTitle" class="subtitle"> | ||
Allow creating a grid dynamically by importing an external CSV or Excel file. This script demo will read the CSV file and will | ||
consider the first row as the column header and create the column definitions accordingly, while the next few rows will be | ||
considered the dataset. Note that this example is demoing a CSV file import but in your application you could easily implemnt | ||
an Excel file uploading. | ||
</div> | ||
|
||
<div class="col-md-6" | ||
style="margin-bottom: 15px"> | ||
<label>Octopart Catalog Search <small>(throttle search to 1.5sec)</small></label> | ||
<input type="text" | ||
class="form-control" | ||
value.bind="search & throttle:1500"> | ||
</div> | ||
<div>A default CSV file can be download <a id="template-dl" href.bind="templateUrl">here</a>.</div> | ||
|
||
<div class="alert alert-danger col-md-7" role="alert"> | ||
<strong>Note:</strong> | ||
this demo no longer displays any results because the WebAPI Key to connect and query the <b>Octopart Component | ||
Search</b> is no longer valid. However the concept remains valid, which is to use your own Custom DataView | ||
instead of the default SlickGrid DataView used by this library. | ||
</div> | ||
<div class="d-flex mt-5 align-items-end"> | ||
<div class="file-upload"> | ||
<label for="formFile" class="form-label">Choose a CSV file…</label> | ||
<input class="form-control" type="file" data-test="file-upload-input" value.bind="uploadFileRef" change.trigger="handleFileImport($event)" /> | ||
</div> | ||
<span class="mx-3">or</span> | ||
<div> | ||
<button id="uploadBtn" data-test="static-data-btn" class="btn btn-outline-secondary" click.trigger="handleDefaultCsv"> | ||
Use default CSV data | ||
</button> | ||
/ | ||
<button class="btn btn-outline-danger btn-sm ms-2" click.trigger="destroyGrid()">Destroy Grid</button> | ||
</div> | ||
</div> | ||
|
||
<div class="alert alert-warning col-md-6" | ||
role="alert" | ||
if.bind="loading"> | ||
<i class="mdi mdi-sync mdi-spin"></i> | ||
<span>Loading...</span> | ||
</div> | ||
<hr /> | ||
|
||
<aurelia-slickgrid grid-id="grid1" | ||
column-definitions.bind="columnDefinitions" | ||
grid-options.bind="gridOptions" | ||
dataset.bind="dataset" | ||
custom-data-view.bind="customDataView" | ||
on-aurelia-grid-created.trigger="aureliaGridReady($event.detail)" | ||
on-viewport-changed.trigger="onViewportChanged()" | ||
on-sort.trigger="onSort($event.detail.eventData, $event.detail.args)"> | ||
</aurelia-slickgrid> | ||
<aurelia-slickgrid | ||
if="value.bind: gridCreated; cache: false" | ||
grid-id="grid17" | ||
column-definitions.bind="columnDefinitions" | ||
grid-options.bind="gridOptions" | ||
dataset.bind="dataset"> | ||
</aurelia-slickgrid> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,147 +1,102 @@ | ||
// import 'slickgrid/slick.remotemodel'; // SlickGrid Remote Plugin | ||
import { bindable, BindingMode } from 'aurelia'; | ||
import { type Column, type GridOption, toCamelCase } from 'aurelia-slickgrid'; | ||
import { ExcelExportService } from '@slickgrid-universal/excel-export'; | ||
|
||
import { | ||
type AureliaGridInstance, | ||
type Column, | ||
type Formatter, | ||
type GridOption, | ||
SlickEventHandler, | ||
} from 'aurelia-slickgrid'; | ||
|
||
const brandFormatter: Formatter = (_row, _cell, _value, _columnDef, dataContext) => { | ||
return dataContext && dataContext.brand && dataContext.brand.name || ''; | ||
}; | ||
|
||
const mpnFormatter: Formatter = (_row, _cell, _value, _columnDef, dataContext) => { | ||
let link = ''; | ||
if (dataContext && dataContext.octopart_url && dataContext.mpn) { | ||
link = `<a href="${dataContext.octopart_url}" target="_blank">${dataContext.mpn}</a>`; | ||
} | ||
return link; | ||
}; | ||
const sampleDataRoot = 'assets/data'; | ||
|
||
export class Example17 { | ||
@bindable({ mode: BindingMode.twoWay }) search = ''; | ||
private _eventHandler: any = new SlickEventHandler(); | ||
|
||
title = 'Example 17: Octopart Catalog Search - Remote Model Plugin'; | ||
subTitle = ` | ||
This example demonstrates how to use "slick.remotemodel.js" or any Remote implementation through an external Remote Service | ||
<ul> | ||
<li>Your browser might block access to the Octopart query, if you get "block content" then just unblock it.</li> | ||
<li>If the demo throws some errors, try again later (there's a limit per day).</li> | ||
<li> | ||
Uses <a href="https://github.com/6pac/SlickGrid/blob/master/slick.remotemodel.js" target="_blank">slick.remotemodel.js</a> | ||
which is hooked up to load search results from Octopart, but can easily be extended | ||
to support any JSONP-compatible backend that accepts paging parameters. | ||
</li> | ||
<li> | ||
This demo implements a custom DataView, however please note that you are on your own to implement all necessary DataView methods | ||
for Sorting, Filtering, etc... | ||
</li> | ||
<li> | ||
Soure code for this example is available <a href="https://github.com/ghiscoding/aurelia-slickgrid/blob/master/doc/github-demo/src/examples/slickgrid/example17.ts" target="_blank">here</a> | ||
</li> | ||
</ul> | ||
`; | ||
aureliaGrid!: AureliaGridInstance; | ||
columnDefinitions: Column[] = []; | ||
customDataView: any; | ||
dataset = []; | ||
gridObj: any; | ||
gridOptions!: GridOption; | ||
loaderDataView: any; | ||
loading = false; // spinner when loading data | ||
|
||
constructor() { | ||
// define the grid options & columns and then create the grid itself | ||
this.defineGrid(); | ||
// this.loaderDataView = new Slick.Data.RemoteModel!(); | ||
// this.customDataView = this.loaderDataView && this.loaderDataView.data; | ||
} | ||
|
||
attached() { | ||
this.hookAllLoaderEvents(); | ||
|
||
// set default search | ||
// this.search = 'switch'; | ||
// this.loaderDataView.setSearch(this.search); | ||
gridCreated = false; | ||
showSubTitle = true; | ||
dataset: any[] = []; | ||
paginationPosition: 'bottom' | 'top' = 'top'; | ||
templateUrl = `${sampleDataRoot}/users.csv`; | ||
uploadFileRef = ''; | ||
|
||
destroyGrid() { | ||
this.gridCreated = false; | ||
} | ||
|
||
detaching() { | ||
// unsubscribe all SlickGrid events | ||
this._eventHandler.unsubscribeAll(); | ||
handleFileImport(event: any) { | ||
const file: File = event.target.files[0]; | ||
if (file.name.endsWith('.csv')) { | ||
const reader = new FileReader(); | ||
reader.onload = (e: any) => { | ||
const content = e.target.result; | ||
this.dynamicallyCreateGrid(content); | ||
}; | ||
reader.readAsText(file); | ||
} else { | ||
alert('File must be a CSV file'); | ||
} | ||
} | ||
|
||
aureliaGridReady(aureliaGrid: AureliaGridInstance) { | ||
this.aureliaGrid = aureliaGrid; | ||
this.gridObj = aureliaGrid.slickGrid; // grid object | ||
// this.loaderDataView.setSort('score', -1); | ||
// this.gridObj.setSortColumn('score', false); | ||
|
||
// simulate a delayed search to preload the first page | ||
window.setTimeout(() => this.searchChanged(this.search), 100); | ||
handleDefaultCsv() { | ||
const staticDataCsv = `First Name,Last Name,Age,Type\nBob,Smith,33,Teacher\nJohn,Doe,20,Student\nJane,Doe,21,Student`; | ||
this.dynamicallyCreateGrid(staticDataCsv); | ||
this.uploadFileRef = ''; | ||
} | ||
|
||
defineGrid() { | ||
this.columnDefinitions = [ | ||
{ id: 'mpn', name: 'MPN', field: 'mpn', formatter: mpnFormatter, width: 100, sortable: true }, | ||
{ id: 'brand', name: 'Brand', field: 'brand.name', formatter: brandFormatter, width: 100, sortable: true }, | ||
{ id: 'short_description', name: 'Description', field: 'short_description', width: 520 }, | ||
]; | ||
dynamicallyCreateGrid(csvContent: string) { | ||
// dispose of any previous grid before creating a new one | ||
this.gridCreated = false; | ||
|
||
const dataRows = csvContent?.split('\n'); | ||
const colDefs: Column[] = []; | ||
const outputData: any[] = []; | ||
|
||
// create column definitions | ||
dataRows.forEach((dataRow, rowIndex) => { | ||
const cellValues = dataRow.split(','); | ||
const dataEntryObj: any = {}; | ||
|
||
if (rowIndex === 0) { | ||
// the 1st row is considered to be the header titles, we can create the column definitions from it | ||
for (const cellVal of cellValues) { | ||
const camelFieldName = toCamelCase(cellVal); | ||
colDefs.push({ | ||
id: camelFieldName, | ||
name: cellVal, | ||
field: camelFieldName, | ||
filterable: true, | ||
sortable: true, | ||
}); | ||
} | ||
} else { | ||
// at this point all column defs were created and we can loop through them and | ||
// we can now start adding data as an object and then simply push it to the dataset array | ||
cellValues.forEach((cellVal, colIndex) => { | ||
dataEntryObj[colDefs[colIndex].id] = cellVal; | ||
}); | ||
|
||
// a unique "id" must be provided, if not found then use the row index and push it to the dataset | ||
if ('id' in dataEntryObj) { | ||
outputData.push(dataEntryObj); | ||
} else { | ||
outputData.push({ ...dataEntryObj, id: rowIndex }); | ||
} | ||
} | ||
}); | ||
|
||
this.gridOptions = { | ||
enableAutoResize: true, | ||
autoResize: { | ||
container: '#demo-container', | ||
rightPadding: 10 | ||
}, | ||
enableCellNavigation: true, | ||
enableColumnReorder: false, | ||
enableGridMenu: false, | ||
multiColumnSort: false | ||
gridHeight: 300, | ||
gridWidth: 800, | ||
enableFiltering: true, | ||
enableExcelExport: true, | ||
externalResources: [new ExcelExportService()], | ||
headerRowHeight: 35, | ||
rowHeight: 33, | ||
}; | ||
} | ||
|
||
hookAllLoaderEvents() { | ||
if (this._eventHandler && this._eventHandler.subscribe && this.loaderDataView && this.loaderDataView.onDataLoading && this.loaderDataView.onDataLoaded) { | ||
this._eventHandler.subscribe(this.loaderDataView.onDataLoading, () => this.loading = true); | ||
this._eventHandler.subscribe(this.loaderDataView.onDataLoaded, (_e: Event, args: any) => { | ||
if (args && this.gridObj && this.gridObj.invalidateRow && this.gridObj.updateRowCount && this.gridObj.render) { | ||
for (let i = args.from; i <= args.to; i++) { | ||
this.gridObj.invalidateRow(i); | ||
} | ||
this.gridObj.updateRowCount(); | ||
this.gridObj.render(); | ||
this.loading = false; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
searchChanged(newValue: string) { | ||
if (newValue && this.gridObj && this.gridObj.getViewport && this.loaderDataView && this.loaderDataView.ensureData && this.loaderDataView.setSearch) { | ||
const vp = this.gridObj.getViewport(); | ||
this.loaderDataView.setSearch(newValue); | ||
this.loaderDataView.ensureData(vp.top, vp.bottom); | ||
} | ||
} | ||
|
||
onSort(_e: Event, args: any) { | ||
if (this.gridObj && this.gridObj.getViewport && this.loaderDataView && this.loaderDataView.ensureData && this.loaderDataView.setSort) { | ||
const vp = this.gridObj.getViewport(); | ||
if (args && args.sortCol && args.sortCol.field) { | ||
this.loaderDataView.setSort(args.sortCol.field, args.sortAsc ? 1 : -1); | ||
} | ||
this.loaderDataView.ensureData(vp.top, vp.bottom); | ||
} | ||
this.dataset = outputData; | ||
this.columnDefinitions = colDefs; | ||
console.log(this.columnDefinitions, this.dataset) | ||
this.gridCreated = true; | ||
} | ||
|
||
onViewportChanged() { | ||
if (this.gridObj && this.gridObj.getViewport && this.loaderDataView && this.loaderDataView.ensureData) { | ||
const vp = this.gridObj.getViewport(); | ||
this.loaderDataView.ensureData(vp.top, vp.bottom); | ||
} | ||
toggleSubTitle() { | ||
this.showSubTitle = !this.showSubTitle; | ||
const action = this.showSubTitle ? 'remove' : 'add'; | ||
document.querySelector('.subtitle')?.classList[action]('hidden'); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.