Customising URL Using Custom Adapters in Open Event Front-end

Open-Event Front-end uses Ember data for handling Open Event Orga API which abides by JSON API specs. The API has relationships which represent models in the database, however there are some API endpoints for which the URL is not direct. We make use of custom adapter to build a custom URL for the requests.
In this blog we will see how to Implement relationships which do not have a model in the API server. Lets see how we implemented the admin-statistics-event API using custom adapter?

Creating Order-statistics model
To create a new model we use ember-cli command:

ember g model admin-statistics-event

The generated model:

export default ModelBase.extend({
  draft     : attr('number'),
  published : attr('number'),
  past      : attr('number')
})

The API returns 3 attributes namely draft, published & past which represent the total number of drafted, live and past event in the system. The admin-statistics-event is an admin related model.
Creating custom adapter
To create a new adapter we use ember-cli command:

ember g adapter event-statistics-event

If we try to do a GET request the URL for the request will be ‘v1/admin-statistics-event’ which is an incorrect endpoint. We create a custom adapter to override the buildURL method.

buildURL(modelName, id, snapshot, requestType, query) {
  let url = this._super(modelName, id, snapshot, requestType, query);
  url = url.replace('admin-statistics-event', 'admin/statistics/event');
  return url;
}

We create a new variable url which holds the url generated by the buildURL method of the super adapter. We call the super method using ‘this._super’. We will now replace the ‘admin-statistics-event’ with ‘admin/statistics/event’ in url variable. We return the new url variable. This results in generation of correct URL for the request.
Thank you for reading the blog, you can check the source code for the example here.
Resources

Handling Requests for hasMany Relationships in Open Event Front-end

In Open Event Front-end we use Ember Data and JSON API specs to integrate the application with the server. Ember Data provides an easy way to handle API requests, however it does not support a direct POST for saving bulk data which was the problem we faced while implementing event creation using the API.

In this blog we will take a look at how we implemented POST requests for saving hasMany relationships, using an example of sessions-speakers route to see how we saved the tracks, microlocations & session-types. Lets see how we did it.

Fetching the data from the server

Ember by default does not support hasMany requests for getting related model data. However we can use external add on which enable the hasMany Get requests, we use ember-data-has-many-query which is a great add on for querying hasMany relations of a model.

let data = this.modelFor('events.view.edit');
data.tracks = data.event.get('tracks');
data.microlocations = data.event.get('microlocations');
data.sessionTypes = data.event.get('sessionTypes');
return RSVP.hash(data);

In the above example we are querying the tracks, microlocations & sessionTypes which are hasMany relationships, related to the events model. We can simply do a to do a GET request for the related model.

data.event.get('tracks');

In the above example we are retrieving the all the tracks of the event.

Sending a POST request for hasMany relationship
Ember currently does not saving bulk data POST requests for hasMany relations. We solved this by doing a POST request for individual data of the hasMany array.

We start with creating a `promises` array which contains all the individual requests. We then iterate over all the hasMany relations & push it to the `promises` array. Now each request is an individual promise.

let promises = [];

promises.push(this.get('model.event.tracks').toArray().map(track => track.save()));
promises.push(this.get('model.event.sessionTypes').toArray().map(type => type.save()));
promises.push(this.get('model.event.microlocations').toArray().map(location => location.save()));

Once we have all the promises we then use RSVP to make the POST requests. We make use of all() method which takes an array of promises as parameter and resolves all the promises. If the promises are not resolved successfully then we simply notify the user using the notify service, else we redirect to the home page.

RSVP.Promise.all(promises)
  .then(() => {
    this.transitionToRoute('index');
  }, function() {
    this.get('notify').error(this.l10n.t(Data did not save. Please try again'));
  });

The result of this we can now retrieve & create new tracks, microlocations & sessionTypes on sessions-speakers route.

Thank you for reading the blog, you can check the source code for the example here.

Resources

 

Adding JSON API support to ember-models-table in Open Event Front-end

Open Event Front-end project uses ember-models-table for handling all the table components in the application. Although ember-models-table is great for handling server requests for operations like pagination, sorting & filtering, but it does not support JSON API used in the Front-end project.

In this blog we will see how we integrated JSON API standards to ember-models-table. Lets see how we added support for JSON API to table and made requests to the Open Event Orga-server.

Adding JSON API support for filtering & sorting

The JSON API specs follow a strict structure for supporting meta data & filtering options, the server expects an array of objects for specifying the name of the field, operation and the value for filtering. The name attribute specifies the column for which we need to apply the filter. eg we use `name` for the events name in the. `op` attribute specifies the operation to be used for filtration, `val` attribute is used to provide a value for comparison. You can check the list of all the supported operations here.

For implementation of filter we will check if the column filter is being used i.e if the filter string is empty or not, if the string is not empty we add a filter object of the column using the specified specs, else we remove the filter object of the column.

if (filter) {
  query.filter.pushObject({
    name : filterTitle,
    op   : 'ilike',
    val  : `%${filter}%`
  });
} else {
  query.filter.removeObject({
    name : filterTitle,
    op   : 'ilike',
    val  : `%${filter}%`
  });
}

For sort functionally we need to pass a query parameter called `sort` which is a string value in the URL. Sorting can be done in ascending or descending order for which the server expects different values. We pass `sort=name` & `sort=-name` for sorting in ascending order & descending order respectively.

const sortSign = {
  none : '',
  asc  : '-',
  desc : ''
};
let sortedBy = get(column, 'sortedBy');
if (typeOf(sortedBy) === 'undefined') {
  sortedBy = get(column, 'propertyName');
}

Adding support for pagination

The pagination in JSON API is implemented using query parameters `page[size]` & `page[number]` which specify the size of the page & the current page number respectively eg

page[size]=10&page[number]=1

This will load the first ten events from the server in the application.

Once the data is loaded in the application we calculate the number of pages to be rendered. The response from the server has attached meta-data which contains the total number of the events in the following structure:

meta: {
  count: 100
}

We calculate the number of pages by dividing the total count by the size of the page. We check if the number of items is greater than the pageSize, and calculate the number of the pages using the formula `items / pagesize + (items % pagesize ? 1 : 0)`. If the items are less than the pageSize we do not have to calculate the pages and we simply hide the pagination in the footer.

if (pageSize > items) {
  this.$('.pagination').css({
    display: 'none'
  });
} else {
  this.$('.pagination').removeAttr('style');
  pages = parseInt((items / pageSize));
  if (items % pageSize) {
    pages = pages + 1;
  }
}

Adding dynamic routing support to ember-models-table

We may want to use the ember-models-table for dynamic routes like `events/list` route, where we load live, drafted & past events based on the current route. The ember-models-table by default do not support the dynamic routes. To add this we override the didReceiveAttrs() method of the component which is executed every time the component updates. We add reset the pageSize, currentPageNumber and the content of the table, as the routes change.

didReceiveAttrs() {
  set(this, 'pageSize', 10);
  set(this, 'currentPageNumber', 1);
  set(this, 'filteredContent', get(this, 'data'));
}

The result of this we now have tables supporting JSON API in the Open Event Front-end application

Thank you for reading the blog, you can check the source code for the example here.

Resources

Customizing Serializers in Open Event Front-end

Open Event Front-end project primarily uses Ember Data for API requests, which handles sending the request to correct endpoint, serializing and deserializing the request/response. The Open Event API project uses JSON API specs for implementation of the API, supported by Ember data.

While sending request we might want to customize the payload using a custom serializer. While implementing the Users API in the project, we faced a similiar problem. Let’s see how we solved it.

Creating a serializer for model

A serializer is created for a model, in this example we will create a user serializer for the user model. One important thing that we must keep in mind while creating a serializer is to use same name as that of model, so that ember can map the model with the serializer. We can create a serializer using ember-cli command:

ember g serializer user

 
Customizing serializer

In Open Event Front-end project every serializer extends the base serializer application.js which defines basic serialization like omitting readOnly attributes from the payload.

The user serializer provides more customization for the user model on top of application model. We override the serialize function, which lets us manipulate the payload of the request. We use `snapshot.id` to differentiate between a create request & an update request. If `snapshot.id` exists then it is an update request else it is a create request.

While manipulation user properties like email, contact etc we do not need to pass ‘password’ in the payload. We make use of ‘adapterOptions’ property associated with the ‘save()’ method. If the adapterOptions are associated and the ‘includePassword’ is set then we add ‘password’ attribute to the payload.

import ApplicationSerializer from 'open-event-frontend/serializers/application';
import { pick, omit } from 'lodash';

export default ApplicationSerializer.extend({
  serialize(snapshot, options) {
    const json = this._super(...arguments);
    if (snapshot.id) {
      let attributesToOmit = [];
      if (!snapshot.adapterOptions || !snapshot.adapterOptions.includePassword) {
        attributesToOmit.push('password');
      }
      json.data.attributes = omit(json.data.attributes, attributesToOmit);
    } else if (options && options.includeId) {
      json.data.attributes = pick(json.data.attributes, ['email', 'password']);
    }
    return json;
  }
});

If we want to add the password in the payload we can simply add ‘includePassword’ property to the ‘adapterOptions’ and pass it in the save method for operations like changing the password of the user.

user.save({
  adapterOptions: {
    includePassword: true
  }
})

Thank you for reading the blog, you can check the source code for the example here.
Resources

Learn more about how to customize serializers in ember data here

Implementing Registration API in Open Event Front-end

In this post I will discuss how I implemented the registration feature in Open Event Front-end using the Open-Event-Orga API. The project uses Ember Data for consumption of the API in the ember application. The front end sends POST request to Open Event Orga Server which verifies and creates the user.

We use a custom serialize method for trimming the request payload of the user model by creating a custom user serializer. Lets see how we did it.

Implementing register API

The register API takes username & password in the payload for a POST request which are validated in the register-form component using the semantic-ui form validation. After validating the inputs from the user we bubble the save action to the controller form the component.

submit() {
  this.onValid(() => {
    this.set('errorMessage', null);
    this.set('isLoading', true);
    this.sendAction('submit');
  });
}

In controller we have `createUser()` action where we send a POST request to the server using the save() method, which returns a promise.

createUser() {
  var user = this.get('model');
  user.save()
    .then(() => {
      this.set('session.newUser', user.get('email'));
      this.set('isLoading', false);
      this.transitionToRoute('login');
    })
    .catch(reason => {
      this.set('isLoading', false);
      if (reason.hasOwnProperty('errors') && reason.errors[0].status === 409) {
        this.set('errorMessage', this.l10n.t('User already exists.'));
      } else {
        this.set('errorMessage', this.l10n.t('An unexpected error occurred.'));
      }
    });
}

The `user.save()` returns a promise, therefore we handle it using a then-catch clause. If the request is successful, it executes the `then` clause where we redirect to the login route. If the request fails we check if the status is 409 which translates to a duplicate entry i.e the user already exists in the server.

Serializing the user model using custom serializer

Ember lets us customise the payload using serializers for models. The serializers have serialize function where we can trim the payload of the model. In the user serializer we check if the request is for record creation using `options.includeId`. If the request is for record creation we trim the payload using the lodash `pick` method and pick only email & password for payload for POST request.

serialize(snapshot, options) {
  const json = this._super(...arguments);
  if (options && options.includeId) {
    json.data.attributes = pick(json.data.attributes, ['email', 'password']);
  }
  return json;
}

Thank you for reading the blog, you can check the source code for the example here.

Resources

Retrieving & Creating Events in Open Event Front-end Using Ember Data

Open Event Front-end uses ember data which is a data persistence library for Ember.js. It uses promises to manage loading and saving records using JSON API format. Ember data uses models to which represent the API response to handle all the requests to be made to the server. Lets see how do we retrieve events & create new events using the Orga Server.

Installing ember data

npm install ember-data

Creating a model

We need a model in ember data which is a class containing all the attributes in the API response. We can create a model using ember cli command:

ember g model event

Populating the model

We must add all the attributes from the API response in the newly generated event model. Ember data automatically takes care of the ajax calls with high level of abstraction using promises. 

identifier             : attr('string'),
name                   : attr('string'),
description            : attr('string'),
startTime              : attr('date', { defaultValue: () => moment().add(1, 'months').startOf('day').toDate() }),
endTime                : attr('date', { defaultValue: () => moment().add(1, 'months').hour(17).minute(0).toDate() }),
locationName           : attr('string'),
searchableLocationName : attr('string'),

thumbnail       : attr('string'),
large           : attr('string'),
backgroundImage : attr('string'),
placeholderUrl  : attr('string'),

longitude : attr('number'),
latitude  : attr('number'),

type     : attr('string'),
topic    : attr('string'),
subTopic : attr('string'),

schedulePublishedOn: attr('date'),

organizerName        : attr('string'),
organizerDescription : attr('string'),
email                : attr('string'),

eventUrl      : attr('string'),
ticketUrl     : attr('string'),
codeOfConduct : attr('string'),

state   : attr('string'),
privacy : attr('string'),

licenceDetails : attr(),
copyright      : attr(),

callForPapers : fragment('call-for-speakers'),
version       : fragment('version'),
socialLinks   : fragmentArray('social-link')

We add all the attributes in the Orga server API response to the event model class.

Once we create the model containing all the attributes we are all set to make API requests in our application. We will now see how to make GET & POST requests in Open Event Front-end project.

GET requests

Ember data lets us retrieve records of a single type using store. The records to be fetched must have respective model in the application. We can make a GET request using

this.store.findAll(‘event’)

This will return a model which contains all the events from the Orga Server, it also contains the events from past as well as the events which are unpublished.

this.store.query('event', {
  include : 'event_topic,event_sub_topic,event_type',
  filter  : [
    {
      name : 'starts_at',
      op   : 'ge',
      val  : moment().toISOString()
    },
    {
      name : 'state',
      op   : 'eq',
      val  : 'Published'
    }
  ]
});

 

To get all the published events we use the query method which lets us add query parameters to the request. We will filter the events on the basis of the state and the end time of the event.

POST requests

Ember data lets us create & update records using the createRecord method of the store. We can create an event using
event = this.store.createRecord(‘event’)

This will create a record locally in the application to send a POST request we use the save() method which send the request to the server.

event.set(name, Event Test);
event.save();


Thank you for reading the blog, you can check the source code for the example
here.
We will discuss in detail how to implement API in Open Event Front-end in the next post.

Resources

Multiple dynamic sub routes in Open Event Front-end

Dynamic routing is a section of the path for a route which changes based on the content of a page. Instead of having multiple routes for similar pages we have one route that changes state accordingly. Dynamic routing is extensively used in the Open Event Front-end application, for tables, events & other pages.

Sometimes we might need to have multiple dynamic routes for a particular route in this case we need to do implement a custom route redirection. Let’s see how did we implement it in the events route?

Dynamic routes in events route

The events route has two dynamic subroutes view & list. By default the dynamic route which is declared at the last will be used by ember which is list route in this case. This leads to loss of the view route which uses the ID of the event to display the event details.

this.route('events', function() {
  this.route('view', { path: '/:event_id' }, function() {
  });
  this.route('list', { path: '/:event_state' });
  this.route('import');
});

Here event_id is the ID of an event from the API response, event_state is the state of the list of events created by the user. The event_id is an alphanumeric value which is dynamic in nature and event_state is a string value which is a static value. Due to non overlapping states we can implement a simple procedure that enables us to create multiple dynamic routes.

Implementing multiple dynamic routing in ‘events’ route

As both the subroutes use different states i.e event_id & event_state, we can use this to redirect the routes accordingly. We will add a beforeModel() function in the events/list route and check if the eventState is not equal to live, draft or past which are the states of list route.

beforeModel(transition) {
   const eventState = transition.params[transition.targetName].event_state;
  if (!['live', 'draft', 'past'].includes(eventState)) {
    this.replaceWith('events.view', eventState);
  }
}

If the ‘eventState’ is not in the array we redirect the page to the events.view route using the replaceWith() function. This will ensure that the events.view route is not lost & the event details are rendered properly.

Thank you for reading the blog, you can check the source code for the example here.

Resources

Implementing Email Preferences in Open Event Front-end

Open Event Front-end lets users customise the email preferences for the notifications for the events created by the user. The user can toggle the notifications for new paper is submitted, change in schedule of sessions,  and reminder for event. In this post we will implement a functional UI for the email preferences in the application. How did we do it?

Creating email-preferences component

We create a component for the email-preferences component using ember-cli command:

ember g component settings/email-preferences-section


Mock response from server

Each event has notification preferences for three services new papers, change in schedule & new event which are stored as a boolean in the server. Each response model contains all the flags for the email preferences.


Implementing UI for email preferences

We use ui-accordion for each event created by the user. The ui-accordion lets us toggle the display for content section, similar to jquery’s collapse-in class. You can find more about semantic ui’s ui-accordion here.Implementing UI for email-preferences-section

model() {
 return [{
   name             : Techtoma,
   role             : Organiser,
   isNewPaper       : true,
   isChangeSchedule : true,
   isNewEvent       : true
 }];

Each preference can be toggled using ui-checkbox from semantic ui. When a checkbox is toggled the respective flag in the model.

<div class="row">
  <div class="column eight wide">
    {{t 'New Paper is Submitted to your Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isNewPaper onChange=(action (mut event.isNewPaper))}}
  </div>
</div>
<div class="row">
  <div class="column eight wide">
    {{t 'Change in Schedule of Sessions in your Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isChangeSchedule onChange=(action (mut event.isChangeSchedule))}}
  </div>
</div>
<div class="row">
  <div class="column eight wide">
    {{t 'Reminder for Next Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isNewEvent onChange=(action (mut event.isNewEvent))}}
  </div>
</div>

If any of the preference is not set i.e false, we toggle the status of the preferences to off. We use math helper or operation for setting the status of the preferences for each preference. We also toggle between the colour of the button between green and yellow, depending on the status of preferences setting.

<a class="ui circular label {{if (or event.isNewPaper event.isChangeSchedule event.isNewEvent) 'green' 'yellow'}}">
  {{#if (or event.isNewPaper event.isChangeSchedule event.isNewEvent)}}
    {{t 'On'}}
  {{else}}
    {{t 'Off'}}
  {{/if}}
</a> 

This results to the following changes on the Open Event Front-end:

The source code for the example is available here.

Resources

Creating custom cells of table in Open Event Front-end

In the previous post I explained how we extended ember-models-table in

Open Event Frontend for rendering & handling all the table related operations like pagination in the application. In this post we will discuss about how we implemented custom cell layout using custom templates in ember-models-table.

We are going to see how we implemented custom cell layouts in Open Event Front-end.

Creating a custom cell template

The cell templates are partial components which allow us to customise the layout of each cell. We create a component using:

ember g component ui-table/cell/cell-event

The cell-event component renders the image of the event along with its name for all events in the table.

cell-event.hbs

<div class="ui header weight-400">
  <img src="{{record.image}}" alt="Event logo" class="ui image"> {{record.name}}
</div>

Each row from the model is defined as record which represents an event. Here we create a header which consists of an event logo which is referenced as record.image in the model & the event name which is referenced as record.name in the template.

Defining columns & custom templates for each cell of the table

After creating all the templates we must specify which columns should use the cell templates. We can define this inside the columns array of objects(each column) to be rendered in the table. Each object has propertyName which represents the id of the column & title which will be rendered as the table header.

columns: [
  {
    propertyName : 'name',
    template     : 'components/ui-table/cell/cell-event',
    title        : 'Name'
  },
  {
    propertyName : 'startTime',
    template     : 'components/ui-table/cell/cell-date',
    title        : 'Date'
  },
  {
    propertyName : 'roles',
    template     : 'components/ui-table/cell/cell-roles',
    title        : 'Roles'
  },
  {
    propertyName : 'sessions',
    template     : 'components/ui-table/cell/cell-sessions',
    title        : 'Sessions'
  },
  {
    propertyName : 'speakers',
    title        : 'Speakers'
  },
  {
    propertyName : 'tickets',
    template     : 'components/ui-table/cell/cell-tickets',
    title        : 'Tickets'
  },
  {
    propertyName : 'url',
    template     : 'components/ui-table/cell/cell-link',
    title        : 'Public URL'
  }
]

The template field for each column is used to pass the reference of template file which will be used to render the cell of the column. All the cell templates are created in components/ui-table/cell which can be found here.

Rendering the table with cell templates

{{events/events-table columns=columns data=model
  useNumericPagination=true
  showGlobalFilter=true
  showPageSize=true
}}

To render  the table with custom cell templates we need to pass the column array to the extended ember-models-table inside the route as :

The outcome of this change on the cells of column Name now looks like this:

Thank you for reading the blog, you can check the source code for the example here.

Resources

https://semantic-ui.com

https://github.com/onechiporenko/ember-models-table

Extending ember-models-table for semantic UI in Open Event Front-end

Open Event Front-end uses ember-models-table for handling all its tables. We chose models-table as there are many tables that are used to display relevant data in the project, which provides features like sorting, searching and pagination common to all table. The ember-models-table allows us handle all such functions using a component.

Although the ember-models-table is great for all kinds of table, it does not support semantic ui which is used in the project. How did you make ember-models-table compatible with semantic ui?

Adding ember-models-table to the project

To add the ember-models-table to the project use:

ember install ember-models-table

Extending the models-table component

The models-table component of the ember-models-table is wrapper which handles all the table functions like search & sort. We extend the models-table component and override the properties of the component.

Create a table component

ember g component ui-table

import Ember from 'ember';
import TableComponent from 'ember-models-table/components/models-table';
import layout from 'open-event-frontend/templates/components/ui-table';

export default TableComponent.extend({
  layout
});

We extend the models-table component by importing it to the ui-table component. We define the layout which is the handlebars template of the table that will be rendered.

<div class="{{classes.outerTableWrapper}}">
  <div class="{{classes.globalFilterDropdownWrapper}}">
    {{#if showPageSize}}
      {{partial pageSizeTemplate}}
    {{/if}}
    {{#if showGlobalFilter}}
      {{partial globalFilterTemplate}}
    {{/if}}
  </div>
  <div class="ui row">
    <div class="{{classes.innerTableWrapper}}">
      <table class="{{classes.table}}">
        <thead class="{{if noHeaderFilteringAndSorting 'table-header-no-filtering-and-sorting'}} {{classes.thead}}">
          {{#if groupedHeaders.length}}
            {{partial headerGroupedRowsTemplate}}
          {{/if}}
          {{partial headerSortingRowTemplate}}
          {{#if useFilteringByColumns}}
            {{partial headerFilteringRowTemplate}}
          {{/if}}
        </thead>
        <tbody>
          {{#if allColumnsAreHidden}}
            {{partial allColumnsHiddenTemplate}}
          {{else}}
            {{#if visibleContent.length}}
              {{#each visibleContent as |record index|}}
                {{#if record}}
                  {{#if (gte index 0)}}
                    {{partial rowTemplate}}
                  {{/if}}
                {{/if}}
              {{/each}}
            {{else}}
              {{partial noDataShowTemplate}}
            {{/if}}
          {{/if}}
        </tbody>
        <tfoot>
          {{partial tableFooterTemplate}}
        </tfoot>
      </table>
    </div>
  </div>
  {{#if showComponentFooter}}
    {{partial componentFooterTemplate}}
  {{/if}}
</div>
{{yield}}

Overriding properties of models-table

The models-table component is not compatible with semantic ui styling, we add the semantic compatibility by overriding the classes of the component & changing it to semantic ui classes. This ensures that the component renders as expected using the semantic ui styled layout.

const defaultCssClasses = {
  outerTableWrapper              : 'ui ui-table',
  columnsDropdown                : 'ui dropdown right'
  pageSizeSelectWrapper          : 'left aligned',
  paginationWrapperNumeric       : 'column four wide',
  paginationWrapperDefault       : 'column four wide',
  buttonDefault                  : 'ui basic button',
  collapseRow                    : 'collapse-row',
  collapseAllRows                : 'collapse-all-rows',
  expandRow                      : 'expand-row',
  expandAllRows                  : 'expand-all-rows',
  clearFilterIcon                : 'remove circle icon',
  globalFilterDropdownWrapper    : 'ui row stackable grid'
};

These classes are use to render all the components of the table.

Adding sub-subcomponents of the models-table

The models-table components uses sub-components which are used to render different sections of the table. We add all the sub-components listed below from here & add it to ui-table.js :

simplePaginationTemplate: 'components/ui-table/simple-pagination',
numericPaginationTemplate: 'components/ui-table/numeric-pagination',
tableFooterTemplate: 'components/ui-table/table-footer',
componentFooterTemplate: 'components/ui-table/component-footer',
pageSizeTemplate: 'components/ui-table/page-size',
globalFilterTemplate: 'components/ui-table/global-filter',
columnsDropdownTemplate: 'components/ui-table/columns-dropdown'

Rendering the ui-table component
For rending the table we need to insert the component using :

{{ui-table columns=columns data=model
  useNumericPagination=true
  showGlobalFilter=true
  showPageSize=true
}}

The outcome of this change on the Open Event Front-end now looks like this:

Thank you for reading the blog, you can check the source code for the example here.

Resources: