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

Continue ReadingImplementing Email Preferences in Open Event Front-end

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

Continue ReadingCreating custom cells of table in Open Event Front-end

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:

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

Creating sponsors layout in Open Event Front-end

In this blog I discuss how we implemented sponsors layout in Open Event Front-end. The sponsors are fetched from Orga Server API and is handled using Ember JS in the Front-end.

The fetched sponsor is an array of JSON objects which need to be grouped based on the type of the sponsor which is done using the lodash library. How do we implement it?

Creating sponsor-list & sponsor-item components

We create two components sponsor-list which contains all the sponsors & sponsor-item which is used to render each sponsor.

ember g component sponsor-list

ember g component sponsor-item

Grouping the sponsors by type

The API response return an array of the sponsors of the event as :

 sponsors: [
   { name: 'Sponsor 2', 
     Url: '#', 
     logoUrl: 'http://placehold.it/150x60', 
     level: 2, 
     type: 'Gold Sponsor', 
     description: '' 
   }, 
   { name: 'Sponsor 1', 
     url: '#', 
     logoUrl: 'http://placehold.it/150x60', 
     level: 1, 
     type: 'Silver Sponsor', 
     description: '' 
   }
 ]

This response is the list of all sponsors, which is not grouped by the type of the sponsor. We sort and group the array and return a JSON object in the sponsor-list component.

import Ember from 'ember';
import { orderBy, groupBy } from 'lodash';

const { Component, computed } = Ember;

export default Component.extend({

 sponsorsGrouped: computed('sponsors.[]', function() {
   return groupBy(orderBy(this.get('sponsors'), 'level'), 'type');
 })
});

We use lodash orderBy to sort the sponsors by the level and groupBy to convert the array into an JSON object of the grouped sponsors. We compute the grouped object using ember computed property.

Rendering sponsors in public event route

The sponsor array is passed to the sponsors-list component where the sponsors are sorted and grouped. We pass each sponsor from the sponsorsGrouped to the sponsor-item component which renders the logo of the sponsor.

sponsor-list.hbs

<h3 class="ui header">{{t 'Sponsors'}}</h3>
{{#each-in sponsorsGrouped as |key sponsors|}}
 <h4 class="ui header">{{key}}</h4>
 <div class="ui three column stackable grid">
   {{#each sponsors as |sponsor|}}
     {{public/sponsor-item sponsor=sponsor}}
   {{/each}}
 </div>
{{/each-in}}

sponsor-item.hbs

<a href="{{sponsor.url}}">
 <img src="{{sponsor.logoUrl}}" class="ui image  sponsor-image" alt="{{sponsor.name}}">
</a>

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:

 

Continue ReadingCreating sponsors layout in Open Event Front-end

Creating inter-component actions in Open Event Front-end

Open Event Front-end project is built using Ember JS, which lets us create modular components. While implementing the sessions route on the project, we faced a challenge of sending inter-component actions. To solve this problem we used the ember action helpers which bubbles up the action to the controller and sends it to the desired component. How did we solve it?

Handling actions in ember

In ember we can handle actions using the {{action ‘function’}} where function executes every time an action on element is triggered. This can be used to handle actions for the component. You can define actions for elements as:

<a href="#" class="item {{if (eq selectedTrackId track.id) 'active'}}" {{action 'filter' track.id}}>
  {{track.name}}
</a>

All the actions defined using the {{action}}  helper are defined inside the actions section of the component. Here the action filter is getting binded to onClick event of the anchor tag. The above helper will pass the name of the track(track) as a parameter to the filter function defined in the component.

Whenever the element is clicked the filter function defined in the component will get triggered. This method works for handling actions within a component, however when we need to trigger inter-component actions this approach does not help.

Sending actions from component to controller

We will send an action from the component to the controller of the route in which the component is rendered using a computed property defined inside the controller, which watched the selectedTrackId.

{{public/session-filter selectedTrackId=selectedTrackId tracks=tracks}}

Whenever the anchor is clicked it passes the id of the selected track to the filter function of the component. Inside the filter function we set the selectedTrackId variable passed to the component inside the route template as specified above.

actions: {
  filter(trackId = null) {
    this.set('selectedTrackId', trackId);
  }
}

The selectedTrackId is a observed by a computed property defined in the controller which modified the track list based on the id passed by the sessions-filter component.

Handling action in the controller

Inside the controller we have a computed property which observes the sessions array and the selectedTrackId which is passed by the session-filter component to the controller called sessionsByTracks.

sessionsByTracks: computed('model.sessions.[]', 'selectedTrackId', function() {
  if (this.get('selectedTrackId')) {
    return chain(this.get('model.sessions'))
      .filter(['track.id', this.get('selectedTrackId')])
      .orderBy(['track.name', 'startAt'])
      .value();
  } else {
    return chain(this.get('model.sessions'))
      .orderBy(['track.name', 'startAt'])
      .groupBy('track.name')
      .value();
  }
}),
tracks: computed('model.sessions.[]', function() {
  return chain(this.get('model.sessions'))
    .map('track')
    .uniqBy('id')
    .orderBy('name')
    .value();
})

sessionsByTracks is the property gets filtered using the lodash functions based upon the id of session passed to it. On the first render all the sessions are displayed as the selectedTrackId is set to null.

{{public/session-list sessions=sessionsByTracks}}

This property is passed to the session-list component which renders the filtered list of session based on the selected session in session-filter component. Check the source code for the example here.

Resources

Continue ReadingCreating inter-component actions in Open Event Front-end

Creating a Responsive Menu in Open Event Frontend

Open Event Frontend uses semantic ui for creating responsive HTML components, however there are some components that are not responsive in nature like buttons & menus. Therefore we need to convert tabbed menus to a one column dropdown menu in mobile views. In this post I describe how we make menus responsive. We are creating a semantic UI custom styling component in Ember to achieve this.

In Open Event we are using the tabbed menus for navigation to a particular route as shown below.

Menu (Desktop)

As you can see there is an issue when viewing the menu on mobile screens.

Menu (Mobile)

Creating custom component for menu

To make the menu responsive we created a custom component called tabbed-navigation which converts the horizontal menu into a vertical dropdown menu for smaller screens. We are using semantic ui styling components for the component to implement the vertical dropdown for mobile view.

tabbed-navigation.js

currentRoute: computed('session.currentRouteName', 'item', function() {
  var path = this.get('session.currentRouteName');
  var item = this.get('item');
  if (path && item) {
    this.set('item', this.$('a.active'));
    this.$('a').addClass('vertical-item');
    return this.$('a.active').text().trim();
  }
}),
didInsertElement() {
  var isMobile = this.get('device.isMobile');
  if (isMobile) {
    this.$('a').addClass('vertical-item');
  }
  this.set('item', this.$('a.active'));
},
actions: {
  toggleMenu() {
    var menu = this.$('div.menu');
    menu.toggleClass('hidden');
  }
}

In the component we check if the device is mobile & change the classes accordingly. For mobile devices we add the vertical-item class to all the items in the menu. We set a property called item in the component which stores the selected item of the menu.

We add a computed property called currentRoute which observes the current route and the selected item, and sets the item to currently active route, and returns the name of the current route.

We add an action toggleMenu which is used to toggle the display of the vertical menu for mobile devices.

tabbed-navigation.hbs

We add a vertical menu dynamically for mobile devices with a button and the name of the current selected item which is stored in currentRoute variable, we also toggle between horizontal & vertical menu based on the screen size.

{{#if device.isMobile}}
  <div role="button" class="ui segment center aligned" {{action 'toggleMenu'}}>
    {{currentRoute}}
  </div>
{{/if}}
<div role="button" class="mobile hidden ui fluid stackable {{unless isNonPointing (unless device.isMobile 'pointing')}} {{unless device.isMobile (if isTabbed 'tabular' (if isVertical 'vertical' 'secondary'))}} menu" {{action 'toggleMenu'}}>
  {{yield}}
</div>

tabbed-navigation.scss

.tabbed-navigation {
  .vertical-item {
    display: block !important;
    text-align: center;
  }
}

Our custom component must look like an item of the menu, to ensure this we use display block property which will allow us to place the menu appear below the toggle button. We also center the menu items so that it looks more like a vertical dropdown.

{{#tabbed-navigation}}
  {{#link-to 'events.view.index' class='item'}}
    {{t 'Overview'}}
  {{/link-to}}
  {{#link-to 'events.view.tickets' class='item'}}
    {{t 'Tickets'}}
  {{/link-to}}
  <a href="#" class='item'>{{t 'Scheduler'}}</a>
  {{#link-to 'events.view.sessions' class='item'}}
    {{t 'Sessions'}}
  {{/link-to}}
  {{#link-to 'events.view.speakers' class='item'}}
    {{t 'Speakers'}}
  {{/link-to}}
  {{#link-to 'events.view.export' class='item'}}
    {{t 'Export'}}
  {{/link-to}}
{{/tabbed-navigation}}

To use this component all we need to do is wrap our menu inside the tabbed-navigation component and it will convert the horizontal menu to the vertical menu for mobile devices.

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

Continue ReadingCreating a Responsive Menu in Open Event Frontend

Creating functional components in Ember JS in Open Event Front-end

In the Open Event Front-end using Ember JS we are creating a functional ticket ordering component which lets users select the amount of tickets and update the total & subtotal price accordingly. How are we doing it?

Creating a component

Components in ember are modular widgets which are used to define a single HTML element, as an independent element. Components can be reused without any dependencies across the project. This provides us a modular architecture to define a component once and reuse it across the project.

For creating a new component use:

ember g component ticket-component

In ember all components must have at least one hyphen in their name. This is an Ember convention, but it is an important one as it ensures there are no naming collisions with future HTML elements. After execution of the above command three new files are generated in the project under:

  • app/components: Contains the logic of the component.
  • app/templates: Contains the view of the component.
  • tests/integration/components: Contains all tests related to component.

Passing an array of ticket detail to template

We can pass data from APIs to the templates using ember-data, but for simplicity we are passing an array of dummy data to get it working. To pass the array to the template we need to add the following JSON to the application controller under controllers/application.js in the application.

tickets:[
  {
    description: 'Discounted ticket for all community members',
    date: 'Mon, May 22',
    price: 40.50,
    name: 'Community Ticket',
    type: 'paid',
    id: 1,
    quantity: 10,
    orderQuantity: 0,
    min: 0,
    max: 5
  }
]

For passing the data to the component template we need to pass the above object to the template using the following syntax in the application.hbs route.

{{public/ticket-component tickets=tickets}}

Rendering ticket details

We iterate over the tickets array passed by application.hbs using each handlebars helper, and we are also using semantic ui dropdown.

For computing subtotals we are using a ember-math-helpers which can be installed using

ember install ember-math-helpers

The semantic ui dropdown is used for selecting the number of orders. This allows us to bind a hidden field for storing the selected value from the dropdown.

{{#ui-dropdown class='compact selection' forceSelection=false}}
 {{input type='hidden' id=(concat ticket.id '_quantity') value=ticket.orderQuantity}}
{{/ui-dropdown}}

For downloading semantic-ui to the project we use:

ember install semantic-ui-ember

Computing subtotal using ember-math-helpers using the mult function which multiplies the price of tickets and the number of order selected from the dropdown.

<td id='{{ticket.id}}_subtotal' class="ui right aligned">
  $ {{number-format (mult ticket.orderQuantity ticket.price)}}
</td>

Computing total price of ordered tickets

To compute total price we are using computed properties which lets us observe properties in the component and compute other properties which are dependent on it. We add a computed property for the orderQuantity property associated with each of the tickets and update the total accordingly.

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

This ensures an update of the total if any changes are made to the orderQuantity property of any ticket.

 

Continue ReadingCreating functional components in Ember JS in Open Event Front-end