Skip to content
nnatter edited this page Sep 18, 2015 · 21 revisions

Decorators

When it comes to styling, the husky datagrid follows an highly customizable decorator approach. In fact, there are two different decorator types which are used by the datagrid;

View-decorators are responsible for the data rendering, which means rendering the item-container and the items itself. Secondly there are pagination-decorators. These decorators handle the pagination of the datagrid.

Per default, the datagrid offers two view-decorators (table, thumbnail) and one pagination-decorator (dropdown). The view-decorator of an datagrid instance is set by the options.view-property of the datagrid. The pagination-property is set by the options.pagination-property.

Additionally view-decorators can be customized by the datagrid options.viewOptions-property. Analogous the options.paginationOptions-property is passed to pagination-decorator instances.

External decorators

Well, two view-decorators and one pagination-decorator aren't that highly customizable. But here's the point; the datagrid allows you to use your awesome, non-default, self-programmed decorators.

Beneath, you find everything you need to know, if your planning to write your own datagrid-decorator. Also, there are already bundle-specific external-decorators in contact-bundle (card-view) and media-bundle (masonry-view) which could be helpful while reaching for your (decorator-) dreams.

Integration

Providing an external decorator to the datagrid isn't that hard. Basically there are 3 steps before you can start implementing your decorator;

  • create the decorator file

A datagrid decorator is a simple javascript-file in its own folder. The recommended path to a view-decorator file is "js/components/example-view/example-view.js". As you can guess, the recommenden path to a pagination-decorator is "js/components/example-pagination/example-pagination.js".

  • define the path to your decorator

Decorators are loaded by require.js on demand. Therefore it is necessary to register a require-path to the decorator file before the datagrid can load it.

require.config({
    paths: {
        'datagrid/decorators/example-view': '../../suluexample/js/components/example-view/example-view'
        'datagrid/decorators/example-pagination': '../../suluexample/js/components/example-pagination/example-pagination'
    }
});
  • pass the decorator to the datagrid

To use your decorator, simply set the options.view/options.pagination-property of your datagrid to your require-path.

this.sandbox.sulu.initListToolbarAndList.call(this, 'key', 'fields',
	{
		// toolbar-options
	},
	{
		// datagrid-options
		view: 'datagrid/decorators/example-view'
                    pagination: 'datagrid/decorators/example-pagination'
	}
);

Implementation

As you might have suspected, your decorator file has to implement a few methods which are used by the datagrid.

This is the recommended basic structure of an view-decorator file (methods which are used by the datagrid are mentioned at the beginning of the code block):

/**
 * @class ExampleView (Datagrid Decorator)
 * @constructor
 *
 * @param {Object} [viewOptions] Configuration object
 *
 * @param {Boolean} [rendered] property used by the datagrid-main class
 * @param {Function} [initialize] function which gets called once at the start of the view
 * @param {Function} [render] function to render data
 * @param {Function} [addRecord] function to add a new record to the grid
 * @param {Function} [removeRecord] function to remove an existing record from the grid
 * @param {Function} [extendOptions] function to extend the decorator options
 * @param {Function} [selectRecord] function to select an existing record
 * @param {Function} [deselectRecord] function to deselct an existing record
 * @param {Function} [deselectAllRecords] function to deselect all existing records
 * @param {Function} [destroy] function to destroy the view and unbind events
 */
 
define(function() {

	'use strict';

	var defaults = {
			// define your default options here
		},

		constants = {
			// define your constants (e.g. dom-selectors) here
		},

		templates = {
			// define your underscore-templates here
		},

		/**
		 * Apply datagrid-content-filters on the given record column by column
		 * datagrid-content-filters are used to format the raw database-values (e.g. size)
		 * @param record
		 * @returns {*}
		 */
		processContentFilters = function(record) {
			var item = this.sandbox.util.extend(false, {}, record);
			this.datagrid.matchings.forEach(function(matching) {
				var argument = (matching.type === this.datagrid.types.THUMBNAILS) ? this.options.imageFormat : '';
				item[matching.attribute] = this.datagrid.processContentFilter.call(
					this.datagrid,
					matching.attribute,
					item[matching.attribute],
					matching.type,
					argument
				);
			}.bind(this));
			return item;
		},

	return {

		/**
		 * Initializes the view, gets called only once
		 * @param {Object} context The context of the datagrid class
		 * @param {Object} options The options used by the view
		 */
		initialize: function(context, options) {
			this.datagrid = context;
			this.sandbox = this.datagrid.sandbox;
			this.options = this.sandbox.util.extend(true, {}, defaults, options);
			
			this.rendered = false;
		},


		/**
		 * Method to render this view
		 * @param data object containing the data which is rendered
		 * @param $container dom-element of the datagrid
		 */
		render: function(data, $container) {
			this.$el = this.sandbox.dom.createElement('<div class="decorator-container"/>');
			this.sandbox.dom.append($container, this.$el);
			
			this.renderRecords(data.embedded);
			this.rendered = true;
		},

		/**
		 * Parses the data and passes it item by item to a render function
		 * @param records {Array} array with records to render
		 * @param appendAtBottom
		 */
		renderRecords: function(records, appendAtBottom) {
			this.sandbox.util.foreach(records, function(record) {
				var item = processContentFilters.call(this, record);
				// render the item to the dom
			}.bind(this));
		},

		/**
		 * Takes an object with options and extends the current ones
		 * @param options {Object} new options to merge to the current ones
		 */
		extendOptions: function(options) {
			this.options = this.sandbox.util.extend(true, {}, this.options, options);
		},

		/**
		 * Destroys the view
		 */
		destroy: function() {
			// unbind your events here
			this.sandbox.dom.remove(this.$el);
		},

		/**
		 * Adds a record to the view
		 * @param record
                     * @param appendAtBottom
		 */
		addRecord: function(record, appendAtBottom) {
			this.renderRecords([record], appendAtBottom);
		},

		/**
		 * Removes a data record from the view
		 * @param recordId {Number|String} the records identifier
		 * @returns {Boolean} true if deleted succesfully
		 */
		removeRecord: function(recordId) {
			if (/* record with recordId exists */) {
				// remove record from dom
				this.datagrid.removeRecord.call(this.datagrid, recordId);
				return true;
			}
			return false;
		},

		/**
		 * Selects an item with a given id
		 * @param id {Number|String} the id of the item
		 */
		selectRecord: function(id) {
			// select record in dom
			this.datagrid.setItemSelected.call(this.datagrid, id);
		},

		/**
		 * Deselect an item with a given id
		 * @param id {Number|String} the id of the item
		 */
		deselectRecord: function(id) {
			// deselect record in dom
			this.datagrid.setItemUnselected.call(this.datagrid, id);
		},

		/**
		 * Deselect all items
		 */
		deselectAllRecords: function() {
			this.sandbox.util.each(this.$items, function(id) {
				this.deselectRecord(Number(id));
			}.bind(this));
		}
	};
});

Analogically this is the recommended basic structure of an pagination-decorator file:

/**
 * @class InfiniteScrollPagination (Datagrid Decorator)
 * @constructor
 *
 * @param {Object} [paginationOptions] Configuration object
 * @param {Number} [options.limit] Data records per page
 *
 * @param {Function} [initialize] function which gets called once at the start of the view
 * @param {Function} [render] function to render data
 * @param {Function} [getHeight] function which returns the height of the pagination
 * @param {Function} [getLimit] function which returns the height of the pagination
 * @param {Function} [destroy] function to destroy the pagination and unbind events
 */
 
define(function() {

	'use strict';

	var defaults = {
			// define your default options here
		},

		constants = {
			// define your constants (e.g. dom-selectors) here
		},

		templates = {
			// define your underscore-templates here
		};

	return {

		/**
		 * Initializes the pagination
		 * @param {Object} context The context of the datagrid
		 * @param {Object} options The options used by this pagination
		 */
		initialize: function(context, options) {
			this.datagrid = context;
			this.sandbox = this.datagrid.sandbox;
			this.options = this.sandbox.util.extend(true, {}, defaults, options);
		},

		* Method to render the pagination
		 * @param data object containing the data which is rendered
		 * @param $container dom-element of the datagrid
		 */
		render: function(data, $container) {
			this.$el = this.sandbox.dom.createElement('<div class="pagination-container"/>');
			this.data = data;
			
			// render your pagination and bind your events
		},
		
		/**
		 * Returns the total height of the pagination
		 * @returns {number}
		 */
		getHeight: function() {
			// return the height of your pagination-container
		},
		
		/**
		 * Returns the pagination page size
		 * @returns {Number} current limit
		 */
		getLimit: function() {
			return this.options.limit;
		},

		/**
		 * Destroys the pagination
		 */
		destroy: function() {
			// unbind your events here
			this.sandbox.dom.remove(this.$el);
		},
	};
});
Clone this wiki locally