Skip to content

Instantly share code, notes, and snippets.

@vegarringdal
Last active July 22, 2016 10:40
Show Gist options
  • Save vegarringdal/8d5fc464893b77101faf4effeec43f27 to your computer and use it in GitHub Desktop.
Save vegarringdal/8d5fc464893b77101faf4effeec43f27 to your computer and use it in GitHub Desktop.
aurelia-v-grid - Demo: Custom pager
<template>
<require from="valueConverters"></require>
<require from="v-grid-control-form.html"></require>
<div class="row">
<v-grid
class="col-md-6"
style="height:350px"
v-row-height="25"
v-header-height="50"
v-footer-height="50"
v-multi-select="true"
v-custom-pager="<own-pager></own-pager>"
v-row-onclick.delegate="singleClick($event.detail)"
v-row-ondblclick.delegate="singleDblClick($event.detail)"
v-current-entity.bind=myCurrentEntity
v-remote-index="id"
v-event-onremote.call="callRemoteServer($event)"
v-row-on-draw.call="onRowDraw($event)"
v-collection.bind=myCollection
v-grid-context.bind=myGrid>
<v-grid-col col-width="50" col-type="selection" col-add-row-attributes="v-key-move"></v-grid-col>
<v-grid-col col-width="120" col-type="text" col-field="name" col-sort="name" col-header-name="Full name" col-filter="name|*|onKeyDown" col-filter-top="false" col-add-row-attributes="v-row-menu='name' v-key-move" col-add-filter-attributes="v-col-header-name-menu='name'"></v-grid-col>
<v-grid-col col-width="100" col-field="number | numberFormat & updateTrigger:'blur':'paste'" col-sort="number" col-header-name="Salery" col-filter="number|>=" col-filter-top="true" col-add-row-attributes="v-row-menu='number' v-key-move" col-add-filter-attributes="v-col-header-name-menu='number'" col-css="color:${tempRef.numberColor};font-weight:${tempRef.numberFont}"></v-grid-col>
<v-grid-col col-width="105" col-type="text" col-field="date | dateFormat & updateTrigger:'blur':'paste'" col-sort="date" col-header-name="Created" col-filter="date|>|dateFormat" col-filter-top="true" col-add-row-attributes="v-row-menu='date' v-key-move" col-add-filter-attributes="v-col-header-name-menu='date'"></v-grid-col>
<v-grid-col col-width="100" col-type="checkbox" col-field="bool" col-sort="bool" col-header-name="Booked" col-filter="bool|=" col-filter-top="true" col-add-row-attributes="v-row-menu='bool' v-key-move" col-add-filter-attributes="v-col-header-name-menu='bool'"></v-grid-col>
<v-grid-col col-width="107" col-type="image" col-field="images" col-header-name="Profile Img" col-filter-top="true" col-add-row-attributes="v-row-menu='images' v-key-move tabindex='0'" col-add-filter-attributes="v-col-header-name-menu='images'"></v-grid-col>
</v-grid>
<!-- Form to the right -->
<v-grid-control-form entity.bind="myCurrentEntity"></v-grid-control-form>
</div>
</template>
import {RemoteData} from 'remoteData'
export class BasicUse {
//utillity functions
myGrid = {};
//current entity, link this to inputs etc
myCurrentEntity = {};
//collection to display
myCollection = [];
//on row draw event, setting some extra fields to tempRef we will use in the css
onRowDraw(data) {
if (data) {
if (data.tempRef) {
if (data.tempRef.number > 100) {
data.tempRef.numberColor = "green";
data.tempRef.numberFont = "normal";
} else {
data.tempRef.numberColor = "red";
data.tempRef.numberFont = "bold";
}
}
}
}
//helper for dummy data
constructor() {
//create a new remote data class
this.remoteData = new RemoteData('https://vgriddummydata-nodedataapi.rhcloud.com/', 'data/people');
}
attached(){
//call our load data
this.loadData();
}
loadData() {
//tell grid to set loading overlay while we get our data
this.myGrid.ctx.setLoadingOverlay(true);
//set limit//offset in our dataclass
this.remoteData.setLimit(40);
this.remoteData.setOffset(0);
//get the data from class
this.remoteData.getData()
.then((data)=>{
//set data to grid (the data have limit % length included)
//-> data ={col:data.result, length:data.length, limit:40}
// you could include offset here if you wanted..
this.myGrid.ctx.setData(data);
})
}
//called by grid
callRemoteServer(param){//filterArray, orderByArray, callback) {
//create orderby string in dataclass
this.remoteData.createOrderByString(param.sort);
//create qquery string in dataclass
this.remoteData.createQueryString(param.filter);
//set limit and offset in dataclass
this.remoteData.setLimit(param.limit);
this.remoteData.setOffset(param.offset);
//call datac,lss and return the data obj.
return this.remoteData.getData()
.then((data)=> {
return data;
}).catch((err)=> {
console.error(err);
console.log(err)
});
}
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://cdn.rawgit.com/vegarringdal/vGridGistRunJSPMBundle/v.1.0.8/jspm_packages/system.js"></script>
<script src="https://cdn.rawgit.com/vegarringdal/vGridGistRunJSPMBundle/v.1.0.8/config.js"></script>
<script>
System.import('aurelia-bootstrapper');
</script>
</body>
</html>
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.globalResources('./mypager')
.plugin('aurelia-v-grid');
aurelia.start().then(a => a.setRoot());
}
<template>
<div class="row">
<div class="btn-group btn-group-justified" role="group">
<button style="width:50px" disabled.bind="!statusFirstButton" click.trigger="firstBtn()" type="button" class="btn btn-primary">${statusFirstButtonTitle}</button >
<button style="width:50px" disabled.bind="!statusPrevButton" click.trigger="prevBtn()" type="button" class="btn btn-primary">${statusPrevButtonTitle}</button >
<button style="width:40px" disabled.bind="!sg0" click.trigger="setG(0)" type="button" class="btn btn-primary">${g0}</button >
<button style="width:40px" disabled.bind="!sg1" click.trigger="setG(1)" type="button" class="btn btn-primary">${g1}</button >
<button style="width:40px" disabled.bind="!sg2" click.trigger="setG(2)" type="button" class="btn btn-primary">${g2}</button >
<button style="width:40px" disabled.bind="!sg3" click.trigger="setG(3)" type="button" class="btn btn-primary">${g3}</button >
<button style="width:40px" disabled.bind="!sg4" click.trigger="setG(4)" type="button" class="btn btn-primary">${g4}</button >
<button style="width:60px" disabled.bind="!statusMiddle" type="button" class="btn btn-primary">${middle}</button >
<button style="width:40px" disabled.bind="!sg5" click.trigger="setG(5)" type="button" class="btn btn-primary">${g5}</button >
<button style="width:40px" disabled.bind="!sg6" click.trigger="setG(6)" type="button" class="btn btn-primary">${g6}</button >
<button style="width:40px" disabled.bind="!sg7" click.trigger="setG(7)" type="button" class="btn btn-primary">${g7}</button >
<button style="width:40px" disabled.bind="!sg8" click.trigger="setG(8)" type="button" class="btn btn-primary">${g8}</button >
<button style="width:40px" disabled.bind="!sg9" click.trigger="setG(9)" type="button" class="btn btn-primary">${g9}</button >
<button style="width:50px" disabled.bind="!statusNextButton" click.trigger="nextBtn()" type="button" class="btn btn-primary">${statusNextButtonTitle}</button >
<button style="width:50px" disabled.bind="!statusLastButton" click.trigger="lastBtn()" type="button" class="btn btn-primary">${statusLastButtonTitle}</button >
</div>
</div>
</template>
/*****************************************************************************************************************
* VGridFooterPager
* Custom element for use in the footer container
* Created by vegar ringdal
*
****************************************************************************************************************/
import {inject, customElement, bindable} from 'aurelia-framework';
@customElement('own-pager')
@inject(Element)
export class VGridElementFooterPager {
info = "";
//sorry in advance for horrible naming!
//buttons between prev and next
g0 = "-";
g1 = "-";
g2 = "-";
g3 = "-";
g4 = "-";
middle = "<->";
g5 = "-";
g6 = "-";
g7 = "-";
g8 = "-";
g9 = "-";
constructor(element) {
this.element = element;
}
bind(parent) {
this.parent = parent;
this.vGrid = parent.vGrid;
this.vGridConfig = parent.vGrid.vGridConfig;
this.vGrid.vGridPager = this;
}
attached() {
this.statusNextButton = false;
this.statusLastButton = false;
this.statusFirstButton = false;
this.statusPrevButton = false;
this.statusNextButtonTitle = this.getLang("pagerBtnNext") || "Next";
this.statusLastButtonTitle = this.getLang("pagerBtnLast") || "Last";
this.statusFirstButtonTitle = this.getLang("pagerBtnFirst") || "First";
this.statusPrevButtonTitle = this.getLang("pagerBtnLast") || "Prev";
this.pagerStringPage = this.getLang("pagerStringPage") || "Page ";
this.pagerStringOf = this.getLang("pagerStringOf") || " of ";
this.pagerStringTotalEntities = this.getLang("pagerStringTotalEntities") || ", Total entities:";
this.pagerStringPageSize = this.getLang("pagerStringPageSize") || ", page size ";
}
getLang(value){
return this.vGrid.vGridConfig.attLanguage[value];
}
setButtonsAllOver5 = ()=> {
//set status first 5
for (let i = 0; i < 5; i++) {
this["g" + i] = this.page + (i - 5);
this["sg" + i] = true;
if (this["g" + i] > this.pages) {
this["g" + i] = "-";
this["sg" + i] = false
}
}
//set status last 5
let y = 0;
for (let i = 5; i < 10; i++) {
this["g" + i] = this.page + (y + 1);
this["sg" + i] = true;
if (this["g" + i] > this.pages) {
this["g" + i] = "-";
this["sg" + i] = false
}
y++
}
};
setButtonsLessThen10 = () =>{
for (let i = 0; i < 10; i++) {
this["g" + i] = (i + 1);
this["sg" + i] = true;
if (this["g" + i] > this.pages) {
this["g" + i] = "-";
this["sg" + i] = false
}
}
}
setButtonsUnder5AtStart = () =>{
for (let i = 0; i < 10; i++) {
this["g" + i] = (i + 1);
this["sg" + i] = true;
if (this["g" + i] > this.pages) {
this["g" + i] = "-";
this["sg" + i] = false
}
}
}
setButtonsUnder5AtEnd = () =>{
for (let i = 0; i < 10; i++) {
this["g" + i] = (this.pages - 10) + i + 1;
this["sg" + i] = true;
if (this["g" + i] > this.pages) {
this["g" + i] = "-";
this["sg" + i] = false
}
}
}
setMiddlebuttons() {
if (this.pages > 10) {
if (this.page < this.pages - 10) {
if (this.page < 6) {
//under 5 in the start
this.setButtonsUnder5AtStart();
} else {
//over 5 in the start
this.setButtonsAllOver5()
}
} else {
//more then 10, and page is more then top page -10
if (this.page > this.pages - 5) {
//unde 5 at the end
this.setButtonsUnder5AtEnd();
} else {
//over 5 in the end
this.setButtonsAllOver5()
}
}
} else {
//less then 10 pages, usually when someone have run a filter
this.setButtonsLessThen10()
}
//set pager
this.middle = this.page + "#" + this.pages;
}
updatePager(data) {
this.collectionLength = data.length;
this.limit = data.limit;
this.offset = data.offset;
this.page = this.offset ? Math.ceil(this.offset / this.limit) + 1 : 1;
if (this.page === 1) {
this.statusFirstButton = false;
this.statusPrevButton = false;
} else {
this.statusFirstButton = true;
this.statusPrevButton = true;
}
if (this.offset >= this.collectionLength - this.limit) {
this.statusNextButton = false;
this.statusLastButton = false;
} else {
this.statusNextButton = true;
this.statusLastButton = true;
}
this.pages = Math.ceil(this.collectionLength / this.limit);
//do we show page info?
if (!this.vGridConfig.attHidePagerInfo) {
this.info = `${this.pagerStringPage}${this.page}${this.pagerStringOf}${this.pages}${this.pagerStringTotalEntities}${this.collectionLength}${this.pagerStringPageSize}${this.limit}`;
}
//raise event
this.vGrid.raiseEvent("v-remote-collection-event", {
evt: "v-remote-collection-event",
page: this.page,
pages: this.pages,
length: this.collectionLength,
pageSize: this.limit
});
this.setMiddlebuttons()
}
firstBtn() {
this.vGrid.loading = true;
this.vGridConfig.remoteOffset = 0;
this.vGridConfig.remoteCall();
}
nextBtn() {
this.vGrid.loading = true;
this.vGridConfig.remoteOffset = this.vGridConfig.remoteOffset + this.vGridConfig.remoteLimit;
this.vGridConfig.remoteCall();
}
prevBtn() {
this.vGrid.loading = true;
this.vGridConfig.remoteOffset = this.vGridConfig.remoteOffset - this.vGridConfig.remoteLimit;
this.vGridConfig.remoteCall();
}
lastBtn() {
this.vGrid.loading = true;
this.vGridConfig.remoteOffset = this.vGridConfig.remoteLength - this.vGridConfig.remoteLimit;
this.vGridConfig.remoteCall();
}
setG(x){
this.vGrid.loading = true;
this.vGridConfig.remoteOffset = (this["g"+x]-1) * this.vGridConfig.remoteLimit;
this.vGridConfig.remoteCall();
}
}
import {HttpClient, json} from 'aurelia-fetch-client';
//helper class to generate my reuests to the "dummy data server" (open shift)
//for anyone that want to create a own opensift server for same purpose can use this one:
//https://github.com/vegarringdal/v-grid-remote-dummy-data
export class RemoteData {
constructor(baseUrl, dataApi){
this.http = new HttpClient();
this.baseUrl = baseUrl;
this.dataApi = dataApi;
this.queryString = null;
this.orderbyString = null;
this.limit = null;
this.offset = null;
this.length = null;
this.configureHttp()
}
/*
configure http with base url
*/
configureHttp(){
this.http.configure(config => {
config
.withBaseUrl(this.baseUrl)
.withDefaults({
credentials: 'same-origin'
});
});
}
/*
Get data from remote
*/
getData(){
return new Promise((resolve, reject)=>{
var params = '';
if(this.queryString){
params = '?'+this.queryString
}
if(this.orderbyString){
var op = params ? '&':'?';
params = params + op + this.orderbyString;
}
if(this.limit){
var op = params ? '&':'?';
params = params + op + 'sqlLimit=' + this.limit;
}
if(this.offset){
var op = params ? '&':'?';
params = params + op + 'sqlOffset=' + this.offset;
}
console.log('request params:' + params);
var encodedString = params !== '' ? window.encodeURI(params):'';
this.http.fetch(this.dataApi + encodedString)
.then(response => response.json())
.then(data => {
if(data.success = true){
this.length = data.length;
resolve({col:data.result, length:data.length, limit:40});
} else {
this.length = 0;
reject({error:data.error});
}
})
})
}
/*
set limit
*/
setLimit(x){
this.limit = x || 40;
}
/*
set offset
*/
setOffset(x){
this.offset = x || 0;
}
/*
create orderby string
*/
createOrderByString(orderByArray){
if(orderByArray){
var sortString = null;
orderByArray.forEach((param, index)=> {
if (index === 0) {
sortString = 'sqlOrderby='
} else {
sortString = sortString + ','
}
sortString = sortString + `${param.attribute} ${param.asc ? "asc" : "desc"}`
});
this.orderbyString = sortString;
} else {
this.orderbyString = null;
}
}
/*
create query string
*/
createQueryString(queryArray){
if (queryArray) {
var queryString = null;
queryArray.forEach((param, index)=> {
if (index === 0) {
queryString = 'sqlQuery=';
} else {
queryString = queryString + ' and '
}
switch (param.operator) {
case "=": //"equals"
queryString = queryString + `${param.attribute} = "${param.value}"`;
break;
case "*": //"contains"
queryString = queryString + `${param.attribute} LIKE "%${param.value}%"`;
break;
case "!=": //"not equal to"
queryString = queryString + `${param.attribute} IS NOT "${param.value}"`;
break;
case "<": //"less than"
queryString = queryString + `${param.attribute} ${param.operator} "${param.value}"`;
break;
case ">": //"greater than"
queryString = queryString + `${param.attribute} ${param.operator} "${param.value}"`;
break;
case "<=": //"less than or eq"
queryString = queryString + `${param.attribute} ${param.operator} "${param.value}"`;
break;
case ">=": //"greater than or eq"
queryString = queryString + `${param.attribute} ${param.operator} "${param.value}"`;
break;
case "*=": //"begins with"
queryString = queryString + `${param.attribute} LIKE "${param.value}%"`;
break;
case "=*": //"ends with"
queryString = queryString + `${param.attribute} LIKE "%${param.value}"`;
break;
case "!*": //"no contain"
queryString = queryString + `${param.attribute} IS NOT "%${param.value}%"`;
break;
}
});
this.queryString = queryString;
} else {
this.queryString = null;
}
}
}
<template bindable="entity">
<form class="col-md-6">
<div style="height:350px" class="well well-sm ">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" id="name" value.bind="entity.name" disabled.bind="!entity.__vGridKey">
</div>
<div class="form-group">
<label for="Number">Number</label>
<input class="form-control" id="Number" value.bind="entity.number" disabled.bind="!entity.__vGridKey">
</div>
<div class="form-group">
<label for="Date">Date</label>
<input class="form-control" id="Date" value.bind="entity.date" disabled.bind="!entity.__vGridKey">
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked.bind="entity.bool" disabled.bind="!entity.__vGridKey"> Bool
</label>
</div>
<img if.bind="entity.__vGridKey" src.bind="entity.images" alt="images" class="img-circle">
</div>
</form>
</template>
/**
* Created by vegar on 6/8/2016.
*/
import moment from 'moment';
export class DateFormatValueConverter {
toView(value) {
if(value){
var x = moment(value).format('DD.MM.YYYY');
return x;
} else {
return value;
}
}
fromView(value) {
if(value){
return new Date(moment(value,'DD.MM.YYYY')._d);
} else {
return value;
}
}
}
import numeral from 'numeral';
export class NumberFormatValueConverter {
toView(value) {
if (value) {
return numeral(value).format('($0,0.00)');
} else {
return value;
}
}
fromView(value) {
if (value) {
return numeral().unformat(value);
} else {
return value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment