0.0.5 • Published 6 years ago

ember-powerful-table v0.0.5

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

ember-powerful-table

Easy way to dinamically generate tables.

Usage

It's simple: just create a settings object and pass it to powerful-table component.

Let's ignore styles for while, we'll talk about it at the end of this document.
Important now are the features!

Basic

Configure the columns and rows.

// controllers/test.js
export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Send the settings to powerful-table component.

{{!-- templates/test.hbs --}}
{{powerful-table table=myTableSettings}}

Result
Basic

SettingTypeDescription
columnsarray of objectsthe columns the table will show
columns[].titlestringtext on header
columns[].fieldsstring or array of stringscorresponding information on rows
rowsarray of objectsdata to fill the table

Asynchronous Rows

Configure rows as a promise.

export default Ember.Controller.extend({
    myTableSettings: Ember.computed(function(){
        return {
            columns: [
                { title: 'Name', fields: 'name' },
                { title: 'Lastname', fields: 'lastname' },
            ],
            rows: this.loadPeople(),
        }
    }),

    loadPeople() {
        return new Ember.RSVP.Promise((resolve, reject) => {
            Ember.run.later(() => {
                resolve([
                    { name: 'Gustavo', lastname: 'Salgado' },
                    { name: 'Miguel', lastname: 'Salgado' },
                ]);
            }, 3000);
        });
    },
});
SettingTypeDescription
rowspromisewait the promise resolve to fill the table

Formatting Values

The original values on rows can be formatted before showed using columns[].valueFormat.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
				title: 'Money',
				fields: 'money',
				valueFormat: ({value}) => `$${value.toFixed(2)}`,
			},
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', money: 100.73 },
			{ name: 'Pri', lastname: 'Lopes', money: 50 },
		],
	},
});

Result
Formatting Value

SettingTypeDescription
columns[].valueFormatfunctionreceives the value and return it formatted

Intrinsic Value Formats

powerful-table have some native formatations.
The table automatically transforms the value and apply some styles to the column.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
			    title: 'Number', 
			    fields: 'number', 
			    valueFormat: 'number' 
			},
			{ 
			    title: 'Percentage', 
			    fields: 'percentage', 
			    valueFormat: 'percentage'
			},
			{ 
			    title: 'Currency', 
			    fields: 'currency', 
			    valueFormat: 'currency'
			},
			{ 
			    title: 'Date', 
			    fields: 'date', 
			    valueFormat: 'date'
			},
		],
		rows: [
			{ 
			    number: 10, 
			    percentage: 23.7, 
			    currency: 100.51, 
			    date: '2017-09-30' 
			}
		]
	},
});

Result
Intrinsic Value Format

SettingTypeDescription
columns[].valueFormatstringoptions: number, percentage, currency or date
Custom Intrinsic Value Formats

The intrinsic formats have custom options.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
			    title: 'Number', 
			    fields: 'number', 
			    valueFormat: {
					format: 'number',
					decimals: 0,
				},
			},
			{ 
			    title: 'Percentage', 
			    fields: 'percentage', 
			    valueFormat: {
					format: 'percentage',
					decimals: 0,
					symbol: '@', // "@" is just for simulation
				}
			},
			{ 
			    title: 'Currency', 
			    fields: 'currency', 
			    valueFormat: {
					format: 'currency',
					decimals: 0,
					symbol: '€',
				}
			},
			{ 
			    title: 'Date', 
			    fields: 'date', 
			    valueFormat: {
					format: 'date',
					input: 'YYYY-MM-DD',
					output: 'MMMM, DD of YYYY',
				}
			},
		],
		rows: [
			{ 
			    number: 10, 
			    percentage: 23.7, 
			    currency: 100.51, 
			    date: '2017-09-30' 
			}
		]
	},
});

Result
Intrinsic Value Format

SettingTypeDescription
columns[].valueFormatobjectformatation settings
columns[].valueFormat.formatstringoptions: number, percentage, currency or date
Extra options based on format
FormatOptionDescription
numberdecimalsdecimal places count
percentagedecimalsdecimal places count
percentagesymbolchar to concat with the value
currencydecimalsdecimal places count
currencysymbolchar to concat with the value
dateinputinput date format(s) (see: momentjs)
dateouputoutput date format (see: momentjs)
Global Instrinsic Value Formats

You can configure valueFormat settings for all powerful-table on environment.

// config/environment.js
module.exports = function(environment) {
  let ENV = {
    ...
    
    APP: {
      ...
      table: {
        valueFormats: {
          number: {
            decimals: 1,
          },
          percentage: {
            decimals: 1,
            symbol: '',
          },
          currency: {
            decimals: 1,
            symbol: 'R$',
          },
          date: {
            input: 'MM/DD/YYYY',
            output: 'DD/MM/YYYY',
          },
        },
      },
    }
  };
  
  ...
  return ENV;
};

Result
Intrinsic Value Format

Composed Values

The columns[].valueFormat also receives all row data.
You can use to decide the final value or to group values.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Name',
				valueFormat: ({row}) => `${Ember.get(row, 'name')} ${Ember.get(row, 'lastname')}`,
			},
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Composed Value

SettingTypeDescription
columns[].valueFormatfunctionreceives all row data and return a value

Computed Values

To optimize the process columns[].valueFormat is called only when row is set.
If you want to observe some data to change your final value use columns[].valueComputed setting.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Value 1', fields: 'value1' },
			{ title: 'Value 2', fields: 'value2' },
			{ 
				title: 'Total', 
				fields: 'total',
				valueFormat: ({row}) => Ember.get(row, 'value1') * Ember.get(row, 'value2'),
				valueComputed: ['value1', 'value2'],
			},
		],
		rows: [
			{ value1: 10, value2: 20 },
		],
	},
	
	init() {
		this._super(...arguments);
		setTimeout(() => {
			Ember.set(this, 'table.rows.0.value1', 20);
			Ember.set(this, 'table.rows.0.value2', 30);
		}, 3000);
	},
});

First state
Value Computed
Second state (after 3 seconds)
Value Computed

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Value 1', 
				fields: 'value1', 
				valueComputed: 'value1',
			},
			{ 
				title: 'Value 2', 
				fields: 'value2', 
				valueComputed: 'value2',
			},
			{ 
				title: 'Total', 
				fields: 'total',
				valueFormat: ({row}) => Ember.get(row, 'value1') * Ember.get(row, 'value2'),
				valueComputed: ['value1', 'value2'],
			},
		],
		rows: [
			{ value1: 10, value2: 20 },
		],
	},
	
	init() {
		this._super(...arguments);
		setTimeout(() => {
			Ember.set(this, 'table.rows.0.value1', 20);
			Ember.set(this, 'table.rows.0.value2', 30);
		}, 3000);
	},
});

First state
Value Computed
Second state (after 3 seconds)
Value Computed

SettingTypeDescription
columns[].valueComputedstring or array of stringsobserve the data and call valueFormat on every changing

Component Values

For a advanced value presentation you can define a component to receive and show the final value.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Product', 
				fields: 'product', 
			},
			{ 
				title: 'Stars', 
				fields: 'stars',
				component: 'column-stars',
			},
		],
		rows: [
			{
				product: 'Beer',
				stars: 5,
			},
			{
				product: 'Socks',
				stars: 1,
			},
		],
	},
});
// components/column-stars.js
export default Ember.Component.extend({
	_stars: Ember.computed('value', function(){
		let value = Ember.get(this, 'value');
		return new Array(value);
	}),
});
{{!-- templates/components/column-stars.hbs --}}
{{#each _stars as |star|}}
	<span>&#9733;</span>
{{/each}}

Result
Component Value

SettingTypeDescription
columns[].componentstringpath to component; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)

Sorting

All columns (with fields attribute filled) are sortable by default.
To disable the sorting on a column use columns[].sorting.enabled setting.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Name', 
				fields: 'name',
				sorting: { enabled: false },
			},
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Sorting

SettingTypeDescription
columns[].sortingobjectsorting settings
columns[].sorting.enabledbooleandefine if the column can be sorted

Initial Sorting

Show sorted rows on init using sorting settings.

export default Ember.Controller.extend({
	myTableSettings: {
		sorting: {
			col: 0,
			order: 'desc',
		},
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Initial Sorting

SettingTypeDescription
sortingobjectsorting settings
sorting.colnumbercolumn index
sorting.orderstringordination: 'asc' or 'desc'

Trigger Action

Create an action clicking on the cell value.
You can open a modal, do a route transition or whatever you want/need.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
				fields: 'age',
				valueFormat: () => 'get age',
				trigger: ({value, row}) => alert(`${Ember.get(row, 'name')} is ${value} years old`)
			}
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', age: 32 },
			{ name: 'Pri', lastname: 'Lopes', age: 29 },
		],
	},
});

Result
Trigger

SettingTypeDescription
columns[].triggerfunctiontriggered on cell click; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)

Show Details

A column can open/close details using columns[].details settings.
powerful-table send a showDetails status to columns[].valueFormat, so you can change the cell value.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: 'row-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});
{{!-- templates/components/row-detail.hbs --}}
{{row.description}}

Result
Details
Details

SettingTypeDescription
columns[].detailsstringpath to the component
columns[].valueFormatfunctionreceives showDetails status

Details callbacks

Do an action when a detail is opened or closed.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: {
					component: 'row-detail',
					onOpen: ({row}) => alert(`${Ember.get(row, 'name')} details opened!`),
					onClose: ({row}) => alert(`${Ember.get(row, 'name')} details closed!`),
				},
				valueFormat: ({showDetails}) => showDetails ? htmlSafe('&#8681;') : htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});

Result
Details Callback
Details Callback

SettingTypeDescription
columns[].detailsobjectdetails settings
columns[].details.componentstringpath to the component
columns[].details.onOpenfunctiontriggered when details is opened
columns[].details.onClosefunctiontriggered when details is closed

Row Details x Column Details

It's possible to manage the formated value by the showDetails status of a row, a column or the especific cell.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
			    //watch if this row is showing details (any column)
				details: 'row-detail',
				valueFormat: ({row}) => row.showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			},
			{
			    //watch if this especific cell is showing details
				details: 'row-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			},
			{
			    //watch if this column is showing details (any row)
				details: 'row-detail',
				valueFormat: ({col}) => col.showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			}
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});

Result
Row Details x Col Details
Clicking on first detail column
Row Details x Col Details
Clicking on second detail column
Row Details x Col Details
Clicking on third detail column
Row Details x Col Details

ParameterTypeDescription
row.showDetailsbooleanindicate if some column is showing details in this row
col.showDetailsbooleanindicate if some detail is open on this column, independent of the row
showDetailsbooleanindicate if the especific cell is showing details

Details table

Show a powerful-table as a detail.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: 'orders-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ 
				name: 'Bruno', 
				lastname: 'Salgado',
				orders: [
					{
						id: 1,
						total: 1730,
						items: [
							{ id: 1, description: 'Notebook', price: 1000 },
							{ id: 2, description: 'Beer', price: 730 },
						],
					},
				],
			},
			{ 
				name: 'Pri', 
				lastname: 'Lopes', 
				orders: [
					{
						id: 2,
						total: 324.57,
						items: [
							{ id: 3, description: 'Shoes', price: 300 },
							{ id: 4, description: 'Wine', price: 24.57 },
						],
					},
				],
			},
		],
	},
});
// components/orders-detail.js
export default Ember.Component.extend({
	tableOrdersSettings: Ember.computed(function(){
		return {
			columns: [
				{
					details: 'order-items-detail',
					valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
					valueComputed: ['showDetails'],
				},
				{ title: 'ID', fields: 'id' },
				{ 
					title: 'Total',
					fields: 'total',
					valueFormat: ({value}) => `$${value.toFixed(2)}`,
				},
			],
			rows: Ember.get(this, 'row.orders'),
		};
	}),
});
{{!-- templates/components/orders-detail.hbs --}}
<strong>Orders:</strong>
{{powerful-table table=tableOrdersSettings}}
// components/order-items-detail.js
export default Ember.Component.extend({
	tableItemsSettings: Ember.computed(function(){
		return {
			columns: [
				{ title: 'ID', fields: 'id' },
				{ title: 'Item', fields: 'description' },
				{ 
					title: 'Price',
					fields: 'price',
					valueFormat: ({value}) => `$${value.toFixed(2)}`,
				},
			],
			rows: Ember.get(this, 'row.items'),
		};
	}),
});
{{!-- templates/components/order-items-detail.hbs --}}
<strong>Items:</strong>
{{powerful-table table=tableItemsSettings}}

First state
Details Table
Person details
Details Table
Order details
Details Table

No Results

Settings to handle when the table has no data.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [],
		noResults: {
			message: 'No data found',
		},
	},
});

Result
No Results

SettingTypeDescription
noResultsobjectsettings for a empty table
noResults.messagestringtext to warn about empty data
Global No Results

Configure noResults settings for all powerful-table on environment.

// config/environment.js
module.exports = function(environment) {
  let ENV = {
    ...
    APP: {
      ...
      table: {
        noResults: {
          message: 'No data found',
        },
      },
    }
  };

  ...
  return ENV;
};

Styling

Make it pretty!

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Date', 
				fields: 'date', 
				valueFormat: 'date',
				headClass: 'date',
				bodyClass: 'date',
			},
			{ 
				title: 'Description', 
				fields: 'description',
				headClass: 'description',
				bodyClass: 'description',
			},
			{ 
				title: 'Value', 
				fields: 'value', 
				valueFormat: 'currency',
				headClass: 'value',
				bodyClass: {
					'value-positive:value-negative': ({value}) => value >= 0,
				},
			},
		],
		rows: [
			{
				date: '2017-09-01',
				description: 'Salary',
				value: 990,
			},
			{
				date: '2017-09-05',
				description: 'Restaurant',
				value: -100.5,
			},
			{
				date: '2017-09-07',
				description: 'Gasoline',
				value: -37,
			},
		],
	},
});
/* app/styles/app.css */
.ember-powerful-table {
	border: 1px solid #ccc;
	font-family: Tahoma;
	font-size: 12px;
}

.ember-powerful-table thead {
	background-color: #ccc;
}

.ember-powerful-table tr:nth-child(even) {
	background-color: #f2f2f2;
}

.ember-powerful-table th.date:before {
	content: '#';
	display: inline-block;
}
.ember-powerful-table td.date {
	font-weight: bold;
}

.ember-powerful-table .description {
	text-align: center;
}

.ember-powerful-table th.value {
	text-align: right;
}
.ember-powerful-table td.value-positive {
	color: blue;
}
.ember-powerful-table td.value-negative {
	color: red;
}

Result
Styling

SettingTypeDescription
headClassstringheader css classes
bodyClassstringbody css classes
bodyClassobjectthe attribute name is the css classes and the value is a function which determine if it's to apply; separate the classes names by ':' to apply true or false classes, respectively; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)