Implementing the tickets API at the ‘tickets/add-order’ route

In Open Event Frontend, we have the ‘tickets/add-order’ route for a specific event which facilitates us to add the order based on the tickets that we create at the time of creation of event. The tickets are listed at the ‘tickets/add-order’ route where we can select the tickets required for example, ‘free’, ‘paid’, the payment type and proceed to the ticket buyer’s info page.

This is how we achieved implementing the API:
We use table to show the data to the user, the columns of which are Ticket Type, Price, Quantity, Item Total something like:

So, the workflow to achieve this is as follows:

  • Query the tickets for current event.
  • Have a controller to calculate the ‘Grand Total’ of the individual tickets that the user wants to buy.
  • Show the tickets in our table.

Querying the tickets: Since we are using ember data, we query tickets by the following query in our model method of route.

  model() {
    return this.modelFor('events.view').query('tickets', {});
  }

Thus, the above query shows that we get the current event by actually querying the model for route ‘events.view’ which returns the current event and then query the tickets model so that we get the tickets associated with the current event.

Since there is no UI table support for ember data, we are using a custom table for all the tables in Open Event Frontend and pass the data to it. To render the data in tables, we follow the following approach.
In our controller, we have a columns property as:

columns: [
    {
      propertyName : 'name',
      title        : 'Ticket Type'
    },
    {
      propertyName   : 'price',
      title          : 'Price(US$)',
      disableSorting : true
    },
    {
      propertyName : '',
      title        : 'Quantity',
      template     : 'components/ui-table/cell/cell-input-number'
    },
    {
      propertyName : 'itemTotal',
      title        : 'Item Total'
    }
  ]

The propertyName maps the property of the objects returned from the server i.e in our case, the ‘tickets’. Thus, we pass this skeleton of columns and data from the model to our component so as to render the table in view.

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

Also, as seen from the image shown earlier in this blog post, we can see that we also need to calculate the ‘Grand Total’ of the total purchase. Thus, we have a computed property in controller to do this:

  total: computed('model.@each.itemTotal', function() {
    let sum = 0.0;
    this.get('model').forEach(ticket => {
      sum += ticket.get('itemTotal');
    });
    return sum;
  }),

We iterate over the each ‘itemTotal’ in the model and keep on adding it so that the total purchase gets added accordingly. Lastly we show the Grand Total to the user as seen in the image shown earlier in the blog.
Thus, the user can select the tickets and proceed towards the checkout.

Resources:
Ember data official guide
Blog on ember data by Andy Crum

Continue ReadingImplementing the tickets API at the ‘tickets/add-order’ route

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

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