Implementing dynamic forms to edit a speaker using Custom Forms in Open Event Frontend

Open Event Frontend allows the organizer of an event to customise the sessions and speakers form using the custom form API. While creating the event the organiser can select the form fields which he wants to place in the sessions and speakers form. This blog will illustrate how a form to edit the details of a speaker is created. Only those fields are included which were included by the person who created the event during the sessions and speakers creation section.

The first step is to retrieve the fields of the form. Each event has custom form fields which can be enabled on the sessions-speakers page, where the organiser can include/exclude the fields for speakers & session forms.

A query is written in the javascript file of the route admin/speakers/edit to retrieve the required details and create a form. The second query helps to determine the speaker id and include the model of speaker and the attribute values of the speaker with that specific id.

import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Route.extend(AuthenticatedRouteMixin, {
 titleToken(model) {
   var speakerName = model.get('name');
   return this.get('l10n').t('Edit Speaker-'.concat(speakerName));
 },
 async model(params) {
   const eventDetails = this.modelFor('events.view');
   return {
     event : eventDetails,
     form  : await eventDetails.query('customForms', {
       'page[size]' : 50,
       sort         : 'id'
     }),
     speaker: await this.get('store').findRecord('speaker', params.speaker_id)
   };
 }
});

In the frontend we call the form of session and speakers. With the speaker-id being passed from the route, a form is created with the values entered by the user during the speaker creation and the other attributes marked included in the session-speakers wizard.

{{forms/session-speaker-form fields=model.form data=model save=(action 'save') isSpeaker=true includeSpeaker=true isSessionSpeaker=true isLoading=isLoading}}

Finally whenever user edits a speaker and clicks on the save button patch endpoint of the speakers API is called and the new details are saved.

Resources

  • Official Ember Model Table docs: http://onechiporenko.github.io/ember-models-table/v.1
  • Official Ember Data documentation: https://github.com/emberjs/data

Adding the Edit Session route in Open Event Frontend

This blog article will illustrate how to edit a Session in Open Event Frontend, for which a nested route /sessions/edit/<session_id> had to be created. Further an Edit Session Form was created on that route to use the patch end-point of the sessions API of Open Event API.

To get started with it, we simply use the ember CLI to generate the routes

$  ember generate route events/view/sessions/edit

Now router.js file is changed to add the session_id. The router.js file should be looking like this now.

this.route(‘sessions’, function() {
this.route(‘list’, { path: ‘/:session_status’ });
this.route(‘create’);
this.route(‘edit’, { path: ‘/edit/:session_id’ });
});

This means that our routes and sub routes are in place. We are using ember CLI to generate these routes, that means, the template files for them generate automatically. Now these routes exist and we need to write the data in the templates of these routes which will get displayed to the end user.

The routes are nested, the content of the parent route can be made available to all the children routes via the outlet in ember js.

Next, we go to the template file of sessions/edit route which is at templates/events/view/sessions/edit.hbs which displays an Edit Session form.

<form class=”ui form {{if isLoading ‘loading’}}” {{action ‘submit’ on=’submit’}}>


{{input type=’text’ id=’title’ value=data.title}}

{{widgets/forms/rich-text-editor value=data.shortAbstract name=’shortAbstract’}}

{{widgets/forms/rich-text-editor value=data.comments name=’comments’}}

{{input type=’text’ value=data.slidesUrl}}

<button type=”submit” class=”ui teal submit button update-changes”>
{{t ‘Save Session’}}
</button>
</form>

After the editing of different attributes is done Save Session button is clicked for which the action is defined in the controller /controllers/events/view/sessions/edit.js. The patch endpoint was called and the details of the attributes changes were saved.

save() {
this.set(‘isLoading’, true);
this.get(‘model’).save()
.then(() => {
this.get(‘notify’).success(this.get(‘l10n’).t(‘Session details have been saved’));
this.transitionToRoute(‘events.view.sessions’);
})
.catch(() => {
this.get(‘notify’).error(this.get(‘l10n’).t(‘Oops something went wrong. Please try again’));
})
.finally(() => {
this.set(‘isLoading’, false);
});
}
}

Thus after editing the attributes of sessions in the Edit Session form and clicking on the Save Session button the session details are edited.

Resources

  • EmberJS Controllers: https://guides.emberjs.com/v2.12.0/controllers
  • Open Event Server, API docs: https://open-event-api.herokuapp.com

Implementing Speaker Table on Open Event Frontend by Integrating Speakers API

This blog article will illustrate how the Speakers API is integrated in Open Event Frontend, which allows for the speakers and their associated data to be displayed in the speakers table. Also, it illustrates how the basic semantic UI table is converted into an ember model table to accommodate the fetched data from the API. Our discussion primarily will involve the app/routes/events/view/speakers  route to illustrate the process.

The primary end point of Open Event API with which we are concerned with for fetching the the speaker details is:

GET /v1/events/{event_identifier}/speakers

First we need to formulate the filters options while making the API call. There are several tabs present which display all the sessions in a particular state i.e. pending, rejected, accepted and all. Thus the formulated filter options stored in the variable filterOptions are as follows:

let filterOptions = [];
if (params.speakers_sessions_state === 'pending') {
filterOptions = [
{
name : 'state',
op : 'eq',
val : 'pending'
}
];
} else if (params.speakers_sessions_state === 'accepted') {
filterOptions = [
{
name : 'state',
op : 'eq',
val : 'accepted'
}
];
} else if (params.speakers_sessions_state === 'rejected') {
filterOptions = [
{
name : 'state',
op : 'eq',
val : 'rejected'
}
];
} else {
filterOptions = [];
}

Next we need to formulate the required query to actually make the API call. It is important to note here that there is a column for sessions of the speaker in  the speaker table. Sessions are a related field for a user and hence we will make use of the include clause while generating the query, Similarly we will make use of the filter clause to pass on the filter options we generated above in filterOptions.

Since the speakers are being fetched for a particular event, and we are in that event’s dashboard, the speakers in question have a hasMany relationship with the aforementioned event. Thus we can make use of the model already fetched in the view route. So the final query call will be as follows:

 return this.modelFor('events.view').query('speakers', {
include      : 'sessions,event',
filter       : filterOptions,
'page[size]' : 10
});

The page[size] option in the query is for implementing pagination, it ensures that at max 10 speakers are returned at once.

Next, we need to convert the existing table to an ember model table. For this the first step is to delete the entire original table along with the dummy data in it. The events-table ember table component will be re-used to form the base structure of the table as follows:

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

Final steps of conversion table to ember table involve defining the columns of the table. They will be defined in the usual manner, with mandatory title and name attributes. In case the display requirements of the data are in mere simple text, calling a template for displaying the text is not required, however for more complex natured values in the columns, it is advisable to make use of the component, and the technical logic can be handled in the component template itself. For instance, one such field on the speakers table is sessions which are related records and were included especially in the query call. They are not directly accessible by their field names. Thus they must be referred as a child of the record object.

{{#each record.sessions as |session|}}
       {{session.title}}
{{/each}}

Similarly the template for the actions column will have to be created as it requires complex logic to implement actions on those buttons. After defining all the columns in the controller, the final columns are as follows:

columns: [
{
propertyName : 'name',
title       : 'Name'
},
{
propertyName : 'email',
title       : 'Email'
},
{
propertyName : 'mobile',
title       : 'Phone'
},
{
propertyName   : 'sessions',
title         : 'Submitted Sessions',
template       : 'components/ui-table/cell/events/view/speakers/cell-simple-sessions',
disableSorting : true
},
{
propertyName : '',
title       : 'Actions',
template     : 'components/ui-table/cell/events/view/speakers/cell-buttons'
}
]

After performing all these steps, the static table which was holding dummy data has been converted into an ember table and thus features like inbuilt pagination, searching etc. can be used.

Resources

 

How App Social Links are specified in Open Event Frontend

This blog article will illustrate how the various social links are specified in the the footer of Open Event Frontend, using the settings API. Open Event Frontend, offers high flexibility to the admins regarding the settings of the App, and hence the media links are not hard coded, and can be changed easily via the admin settings panel.

The primary end point of Open Event API with which we are concerned with for fetching the settings  for the app is

GET /v1/settings

The model for settings has the following fields which concern the social links.

 googleUrl              : attr('string'),
 githubUrl              : attr('string'),
 twitterUrl             : attr('string')

Next we define them as segmented URL(s) so that they can make use of the link input widget.

segmentedTwitterUrl    : computedSegmentedLink.bind(this)('twitterUrl'),
 segmentedGoogleUrl     : computedSegmentedLink.bind(this)('googleUrl'),
 segmentedGithubUrl     : computedSegmentedLink.bind(this)('githubUrl'),

Now it is required for us to fetch the data from the API, by making the corresponding call to the API. Since the footer is present in every single page of the app, it is necessary that we make the call from the application route itself. Hence we add the following to the application route modal.

socialLinks: this.get('store').queryRecord('setting', {
})

Next we need to iterate over these social links, and add them to the footer as per their availability.So we will do so by first passing the model to the footer component, and then iterating over it in footer.hbs

{{footer-main socialLinks=model.socialLinks footerPages=footerPages}}


And thus we have passed the socialLinks portion of the model, under the alias socialLinks.Next, we iterate over them and each time check, if the link exists before rendering it.

<div class="three wide column">
     <div class="ui inverted link list">
       <strong class="item">{{t 'Connect with us'}}</strong>
       {{#if socialLinks.supportUrl}}
         <a class="item" href="{{socialLinks.supportUrl}}" target="_blank" rel="noopener noreferrer">
           <i class="info icon"></i> {{t 'Support'}}
         </a>
       {{/if}}
       {{#if socialLinks.facebookUrl}}
         <a class="item" href="{{socialLinks.facebookUrl}}" target="_blank" rel="noopener noreferrer">
           <i class="facebook f icon"></i> {{t 'Facebook'}}
         </a>
       {{/if}}
       {{#if socialLinks.youtubeUrl}}
         <a class="item" href="{{socialLinks.youtubeUrl}}" target="_blank" rel="noopener noreferrer">
           <i class="youtube icon"></i> {{t 'Youtube'}}
         </a>
       {{/if}}
       {{#if socialLinks.googleUrl}}
         <a class="item" href="{{socialLinks.googleUrl}}" target="_blank" rel="noopener noreferrer">
           <i class="google plus icon"></i> {{t 'Google +'}}
         </a>
       {{/if}}
     </div>
   </div>

Thus all the links in the app are easily manageable, from the admin settings menu, without the need of hard coding them. This approach also, makes it easy to preserve the configuration in a central location.

Resources

Keeping Order of tickets in Event Wizard in Sync with API on Open Event Frontend

This blog article will illustrate how the various tickets are stored and displayed in order the event organiser decides  on  Open Event Frontend and also, how they are kept in sync with the backend.

First we will take a look at how the user is able to control the order of the tickets using the ticket widget.

{{#each tickets as |ticket index|}}
  {{widgets/forms/ticket-input ticket=ticket
  timezone=data.event.timezone
  canMoveUp=(not-eq index 0)
  canMoveDown=(not-eq ticket.position (dec
  data.event.tickets.length))
  moveTicketUp=(action 'moveTicket' ticket 'up')
  moveTicketDown=(action 'moveTicket' ticket 'down')
  removeTicket=(confirm 'Are you sure you  wish to delete this 
  ticket ?' (action 'removeTicket' ticket))}}
{{/each}}

The canMoveUp and canMoveDown are dynamic properties and are dependent upon the current positions of the tickets in the tickets array.  These properties define whether the up or down arraow or both should be visible alongside the ticket to trigger the moveTicket action.

There is an attribute called position in the ticket model which is responsible for storing the position of the ticket on the backend. Hence it is necessary that the list of the ticket available should always be ordered by position. However, it should be kept in mind, that even if the position attribute of the tickers is changed, it will not actually change the indices of the ticket records in the array fetched from the API. And since we want the ticker order in sync with the backend, i.e. user shouldn’t have to refresh to see the changes in ticket order, we are going to return the tickets via a computed function which sorts them in the required order.

tickets: computed('data.event.tickets.@each.isDeleted', 'data.event.tickets.@each.position', function() {
   return this.get('data.event.tickets').sortBy('position').filterBy('isDeleted', false);
 })

The sortBy method ensures that the tickets are always ordered and this computed property thus watches the position of each of the tickets to look out for any changes. Now we can finally define the moveTicket action to enable modification of position for tickets.

moveTicket(ticket, direction) {
     const index = ticket.get('position');
     const otherTicket = this.get('data.event.tickets').find(otherTicket => otherTicket.get('position') === (direction === 'up' ? (index - 1) : (index + 1)));
     otherTicket.set('position', index);
     ticket.set('position', direction === 'up' ? (index - 1) : (index + 1));
   }

The moveTicket action takes two arguments, ticket and direction. It temporarily stores the position of the current ticket and the position of the ticket which needs to be swapped with the current ticket.Based on the direction the positions are swapped. Since the position of each of the tickets is being watched by the tickets computed array, the change in order becomes apparent immediately.

Now when the User will trigger the save request, the positions of each of the tickets will be updated via a PATCH or POST (if the ticket is new) request.

Also, the positions of all the tickets maybe affected while adding a new ticket or deleting an existing one. In case of a new ticket, the position of the new ticket should be initialised while creating it and it should be below all the other tickets.

addTicket(type, position) {
     const salesStartDateTime = moment();
     const salesEndDateTime = this.get('data.event.startsAt');
     this.get('data.event.tickets').pushObject(this.store.createRecord('ticket', {
       type,
       position,
       salesStartsAt : salesStartDateTime,
       salesEndsAt   : salesEndDateTime
     }));
   }

Deleting a ticket requires updating positions of all the tickets below the deleted ticket. All of the positions need to be shifted one place up.

removeTicket(deleteTicket) {
     const index = deleteTicket.get('position');
     this.get('data.event.tickets').forEach(ticket => {
       if (ticket.get('position') > index) {
         ticket.set('position', ticket.get('position') - 1);
       }
     });
     deleteTicket.deleteRecord();
   }

The tickets whose position is to be updated are filtered by comparison of their position from the position of the deleted ticket.

Resources

Implementing Order Statistics API on Tickets Route in Open Event Frontend

The order statistics API endpoints are used to display the statistics related to tickets, orders, and sales. It contains the details about the total number of orders, the total number of tickets sold and the amount of the sales. It also gives the detailed information about the pending, expired, placed and completed orders, tickets, and sales.

This article will illustrate how the order statistics can be displayed using the Order Statistics API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for statistics is

GET /v1/events/{event_identifier}/order-statistics

First, we need to create a model for the order statistics, which will have the fields corresponding to the API, so we proceed with the ember CLI command:

ember g model order-statistics-tickets

Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the model looks like this:

import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';

export default ModelBase.extend({
  orders  : attr(),
  tickets : attr(),
  sales   : attr()
});

As we need to display the statistics related to orders, tickets, and sales so we have their respective variables inside the model which will fetch and store the details from the API.

Now, after creating a model, we need to make an API call to get the details. This can be done using the following:

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

Since the tickets route is nested inside the event.view route so, first we are getting the model for event.view route and then we’re querying order statistics from the model.

The complete code can be seen here.

Now, we need to call the model inside the template file to display the details. To fetch the total orders we can write like this

{{model.orders.total}}

 

In a similar way, the total sales can be displayed like this.

{{model.sales.total}}

 

And total tickets can be displayed like this

{{model.tickets.total}}

 

If we want to fetch other details like the pending sales or completed orders then the only thing we need to replace is the total attribute. In place of total, we can add any other attribute depending on the requirement. The complete code of the template can be seen here.

The UI for the order statistics on the tickets route looks like this.

Fig. 1: The user interface for displaying the statistics

The complete source code can be seen here.

Resources:

Implementing Pages API in Open Event Frontend

The pages endpoints are used to create static pages which such as about page or any other page that doesn’t need to be updated frequently and only a specific content is to be shown. This article will illustrate how the pages can be added or removed from the /admin/content/pages route using the pages API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for pages is

GET /v1/pages

First, we need to create a model for the pages, which will have the fields corresponding to the API, so we proceed with the ember CLI command:

ember g model page

Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the page model looks like this:

import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';

export default ModelBase.extend({
  name        : attr('string'),
  title       : attr('string'),
  url         : attr('string'),
  description : attr('string'),
  language    : attr('string'),
  index       : attr('number', { defaultValue: 0 }),
  place       : attr('string')
});

As the page will have name, title, url which will tell the URL of the page, the language, the description, index and the place of the page where it has to be which can be either a footer or an event.

The complete code for the model can be seen here.

Now, after creating a model, we need to make an API call to get and post the pages created. This can be done using the following:

return this.get('store').findAll('page');

The above line will check the store and find all the pages which have been cached in and if there is no record found then it will make an API call and cache the records in the store so that when called it can return it immediately.

Since in the case of pages we have multiple options like creating a new page, updating a new page, deleting an existing page etc. For creating and updating the page we have a form which has the fields required by the API to create the page.  The UI of the form looks like this.

Fig. 1: The user interface of the form used to create the page.

Fig. 2: The user interface of the form used to update and delete the already existing page

The code for the above form can be seen here.

Now, if we click the items which are present in the sidebar on the left, it enables us to edit and update the page by displaying the information stored in the form and then the details be later updated on the server by clicking the Update button. If we want to delete the form we can do so using the delete button which first shows a pop up to confirm whether we actually want to delete it or not. The code for displaying the delete confirmation pop up looks like this.

<button class="ui red button" 
{{action (confirm (t 'Are you sure you would like to delete this page?') (action 'deletePage' data))}}>
{{t 'Delete'}}</button>

 

The code to delete the page looks like this

deletePage(data) {
    if (!this.get('isCreate')) {
      data.destroyRecord();
      this.set('isFormOpen', false);
    }
  }

In the above piece of code, we’re checking whether the form is in create mode or update mode and if it’s in create mode then we can destroy the record and then close the form.

The UI for the pop up looks like this.

Fig.3: The user interface for delete confirmation pop up

The code for the entire process of page creation to deletion can be checked here

To conclude, this is how we efficiently do the process of page creation, updating and deletion using the Open-Event-Orga pages API  ensuring that there is no unnecessary API call to fetch the data and no code duplication.

Resources:

Implementing Roles API on Open Event Frontend to Create Roles Using an External Modal

This blog article will illustrate how the roles are created via the external model  on the admin permissions page in Open Event Frontend, using the roles API. Our discussion primarily will involve the admin/permissions/index route to illustrate the process.The primary end point of Open Event API with which we are concerned with for fetching the permissions  for a user is

POST /v1/roles

First we need to create a model for the user-permissions, which will have the fields corresponding to the api, so we proceed with the ember CLI command:

ember g model role

Next we define the model according to the requirements. The model needs to extend the base model class, and has only two fields one for the title and one for the actual name of the role.

import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';

export default ModelBase.extend({
 name           : attr('string'),
 titleName      : attr('string')
 });

Next we need to modify the existing modal to incorporate the API and creation of roles in it. It is very important to note here that using createRecord as the model will result in a major flaw. If createRecord is used and the user tries to create multiple roles, other than the first POST request all the subsequent requests will be PATCH requests and will keep on modifying the same role. To avoid this, a new record needs to be created every time the user clicks on Add Role.  We slightly modify the modal component call to pass in the name and titleName to it.

{{modals/add-system-role-modal  isOpen=isAddSystemRoleModalOpen
                                isLoading=isLoading
                                name=name
                                titleName=titleName
                                addSystemRole=(action 'addSystemRole')}}

Upon entering the details of the roles and successful validation of the form, if the user clicks the Add Role button of the modal, the action addSystemRole will be triggered. We will write the entire logic for the same in the respective controller of the route.

addSystemRole() {
     this.set('isLoading', true);
     this.get('store').createRecord('role', {
       name      : this.get('name'),
       titleName : this.get('titleName')
     }).save()
       .then(() => {
         this.set('isLoading', false);
         this.notify.success(this.l10n.t('User permissions have 
         been saved successfully.'));
         this.set('isAddSystemRoleModalOpen', false);
         this.setProperties({
           name          : null,
           roleTitleName : null
         });
       })
       .catch(()=> {
         this.set('isLoading', false);
         this.notify.error(this.l10n.t('An unexpected error has occurred.
         User permissions not saved.'));
       });
   },

At first the isLoading property is made true.This adds the semantic UI class loading to the the form,  and so the form goes in the loading state, Next, a record is created of the type role  and it’s properties are made equal to the corresponding values entered by the user.

Then save() is called, which subsequently makes a POST request to the server. If the request is successful the modal is closed by setting the isAddSystemRoleModalOpen property to false. Also, the fields of the modal are cleared for a  better user experience in case multiple roles need to be added one after the other.

In cases when  there is an error during the processing of the request the catch() block executes. And the modal is not closed. Neither are the fields cleared.

Resources

Implementing Users API to Display the Users at Admin Route in Open Event Frontend

This article will illustrate how the users are displayed and updated on the /admin/users route, their roles, user links etc. using the users API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for fetching the users is

GET /v1/users

First, we need to create a model for the user, which will have the fields corresponding to the API, so we proceed with the ember CLI command:

ember g model user

Next, we need to define the model according to the requirements. The model needs to extend the base model class. As a user can have multiple notifications, orders and  sessions etc. so we have to use ember data relationships “hasMany”. Hence, the model will have the following format.

import ModelBase from 'open-event-frontend/models/base';
import { hasMany } from 'ember-data/relationships';

export default ModelBase.extend({
  email        : attr('string'),
  password     : attr('string'),
  isVerified   : attr('boolean', { readOnly: true }),
  isSuperAdmin : attr('boolean', { readOnly: true }),
  isAdmin      : attr('boolean', { readOnly: true }),
  firstName : attr('string'),
  lastName  : attr('string')
});

The complete code for the model can be seen here

Now, we need to load the data from the API using the above model, so we will send a GET request to the API to fetch the users. This can be easily achieved using this.

return this.get('store').query('user', {'page[size]': 10 });

The above line is querying for the users from the store which is place where cache of all of the records that have been loaded by our application is there. If a route asks for a record, the store can return it immediately if it is there in the cache and we want to display only 10 users in a page so defined how many number of users has to be loaded at a time.

Now we need to filter the users based on whether they are active or they have deleted their accounts. For this purpose, we need to pass filter to the query which will tell what type of users to be loaded at once.

The next thing we need to do is to display the above data fetched from the API into an ember table. Ember table helps in allowing us to render very large data sets by only rendering the rows that are being displayed. For this, we defined a controller class which will help in letting the table know what all columns will be required to display and the attribute values they correspond in the API. We can also define the template for each column. The code for the controller class looks like this.

import Ember from 'ember';

const { Controller } = Ember;
export default Controller.extend({
  columns: [
    {
      propertyName     : 'first-name',
      title            : 'Name',
      disableSorting   : true,
      disableFiltering : true
    },
    {
      propertyName     : 'email',
      title            : 'Email',
      disableSorting   : true,
      disableFiltering : true
     },
     {
      propertyName     : 'last-accessed-at',
      title            : 'Last Accessed',
      template         : 'components/ui-table/cell/admin/users/cell-last-accessed-at',
      disableSorting   : true,
      disableFiltering : true
    }
   ]
});

In the above code, we can see a field called ‘disableSorting’ which is true if we don’t want to sort the table based on that column. Since we want the last-accessed-at column to be customized, so we have separately added a template for the column which will ensure how it will look in the column. The complete code for the other columns which are there in table apart from these can be found here.

Now to display the ember table we will write the following code.

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

In the above piece of code, we are calling the same ember table as we used in case of events to reduce the code duplication. We are passing the columns and data in the table which remains unique to the table. Next, we are ensuring that our page shows the amount of data we’re fetching at one go, allows the filtering the table based on the columns.

The UI of the users page for the above code snippets look like this.

Fig 1: The UI of the users table under admin/users route

The entire code for implementing the users API can be seen here.

To conclude, this is how we efficiently fetched the users using the Open-Event-Orga users API, ensuring that there is no unnecessary API call to fetch the data and no code duplication using the same ember table again.

Resources:

Implementing Sessions API for the event in Open Event Frontend

This article will illustrate how the sessions are displayed and updated on the events/{event_id}/sessions route to display the sessions available for a particular event using the sessions API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for fetching the sessions is

GET /v1/sessions/{session_id}

First, we need to create a model for the sessions, which will have the fields corresponding to the API, so we proceed with the ember CLI command:

ember g model session

Next, we need to define the model according to the requirements. The model needs to extend the base model class. As a session can have multiple speakers and a session always belongs to an event, so we have to use ember data relationships “hasMany” and “belongsTo”. Hence, the model will have the following format.

import ModelBase from 'open-event-frontend/models/base';
import { belongsTo, hasMany } from 'ember-data/relationships';

export default ModelBase.extend({
  title         : attr('string'),
  subtitle      : attr('string'),

  speakers      : hasMany('speaker'),
  event         : belongsTo('event')
});

Complete code for the model can be seen here

Now, we need to load the data from the API using the above model, so we will send a GET request to the API to fetch the sessions corresponding to a particular event. This can be easily achieved using this.

return this.modelFor('events.view').query('sessions');

The above line is asking for getting the current model that is on the route events.view and query for the sessions property from that model.

Now we need to filter the sessions based on their sessions whether they have been accepted or confirmed or pending or rejected and display them on different pages. For this purpose, we need to pass filter and pages to the query which will tell what type and now of sessions to be loaded at once. Also, we need to display the speakers associated with session and event details. For this case, the above query will be formatted like this.

return this.modelFor('events.view').query('sessions', {
      include      : 'event,speakers',
    filter       : filterOptions,
      'page[size]' : 10
    });  

In the above query, the filterOptions are designed in such a way which check for what type of sessions user is querying for. The code can be found here.

The next thing we need to do is to display the above data fetched from the API into an ember table. For this, we need to have a controller class which will help in letting the table know what all columns will be required to display and the attribute values they correspond in the API. We can also define the template for each column. The code for the controller class looks like this.

export default Controller.extend({
  columns: [
    {
      propertyName   : 'state',
      title          : 'State',
      disableSorting : true,
      template       : 'components/ui-table/cell/events/view/sessions/cell-session-state'
    },
    {
      propertyName : 'title',
      title          : 'Title'
    },
    {
      propertyName    : 'speakers',
      template       : 'components/ui-table/cell/cell-speakers',
      title          : 'Speakers',
      disableSorting  : true
     }]
});

In the above code, we can see a field called ‘disableSorting’ which is true if we don’t want to sort the table based on that column. Since we want the state column to be customized, so we have separately added a template for the column which will ensure how it will look in the column. The complete code for the other columns which are there in table apart from the state, title and speakers can be found here.

Now to display the ember table we will write the following code.

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

In the above piece of code, we are calling the same ember table as we used in case of events to reduce the code duplication. We are passing the columns and data in the table which remains unique to the table. Next, we are ensuring that our page shows the amount of data we’re fetching at one go, allows the filtering the table based on the columns.

The UI of the sessions page for the above code snippets look like this.

Fig 1: The UI of the session table under events/{event_id}/session route

The entire code for implementing the sessions API can be seen here.

To conclude, this is how we efficiently fetched the sessions details using the Open-Event-Orga sessions API, ensuring that there is no unnecessary API call to fetch the data and no code duplication using the same ember table again.

Resources: