Custom Handlebar Helpers used in Open Event Frontend

In Open Event Frontend, we are using custom handlebars like ‘confirm’, ‘css’, ‘includes’, ‘sanitize’, ‘text-color’, etc. Custom helpers are used to format any quantity for example ‘capitalizing the first letter of a word’, ‘reducing a number’s fractional part’, ‘making a text bold’, etc. Thus, with the help of Custom Helpers we can avoid manipulating data in controllers. We use Custom Helpers in the Handlebars’ braces before the property to be formatted. In this blog, I will be explaining one of the custom helpers used in Open Event Frontend.

Creation of custom helper :

Since ember provides an efficient and easy way to create components, controllers, routes through it’s ember-cli, we can also create helpers through a single command.

ember g helper url-encode

This will generate a helper in our app i.e two files. First one url-encode.js where all of our logic goes for helper and the other one url-encode-test.js where the tests for it are written.

Following are the two files you are going to see once you run above commands.

import Ember from 'ember';

const { Helper } = Ember;

export function urlEncode(params) {
  
}

export default Helper.helper(urlEncode);

      url-encode.js

import { test } from 'ember-qunit';
import moduleForComponent from 'open-event-frontend/tests/helpers/component-helper';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('url-encode', 'helper:url-encode');

test('it renders', function(assert) {
  this.set('inputValue', 'hello world');
  this.render(hbs`{{url-encode inputValue}}`);
  assert.equal(this.$().text().trim(), 'hello%20world');
});

        url-encode-test.js

Now all the logic for the helper goes in url-encode.js. But before that, we pass the helper in the template and pass the params to the url-encode.js to process the data and return it.

Use in template:

<a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/sharer.php?u={{url-encode event.url}}"></a>

      Fig. Use of helpers

So, basically in the above code, we have used the helper “url-encode” and have passed the parameter “event.url” to it which we are getting from the model. Now we need to get this parameter in the url-encode.js file where we will encode it and return.

Accessing the params in helper:

import Ember from 'ember';

const { Helper } = Ember;

/**
 * Helper to URL encode a string
 * @param params
 * @returns {*}
 */
export function urlEncode(params) {
  if (!params || params.length === 0) {
    return '';
  }
  return encodeURIComponent(params[0]);
}

export default Helper.helper(urlEncode);

The above code shows how we can access the params in the url-encode.js. ‘params’ is an array passed which can be accessed in the function directly. Now we just need to perform the action on the params and return it to the template. Here we are using ‘encodeURIComponent’ method to encode the URL and then returning it.
This is how the helpers work. They are a better way to reduce redundancy and help code structure better.

Resources:

Ember JS Official guide

Blog on DockYard about Helpers by Lauren Tan

Source code:

https://github.com/fossasia/open-event-frontend/blob/development/app/helpers/url-encode.js

https://github.com/fossasia/open-event-frontend/blob/development/tests/integration/helpers/url-encode-test.js

Adding Settings and Contact Info Route to Open Event Frontend

In Open Event Frontend, while dealing with an issue, we had to create the ‘contact-info’ route as a subroute of the ‘settings’ route. We have given the user a facility to update his/her information thereby allowing them to change it whenever they want.

Thus to generate the route, we are using ember cli:

ember g route settings/contact-info

Thus, the above command will generate three files:

  • routes/settings/contact-info.js
  • templates/settings/contact-info.hbs
  • tests/unit/routes/settings/contact-info-test.js

1) contact-info.hbs

In this file, we have a form which we have made a component for easy use. Thus following is the content of the contact-info.hbs file:

{{settings/contact-info-section}}

Thus ultimately, we are having a component called ‘contact-info-section’ which contains our markup for the contact-info form.
To generate the component, we do:

ember g component settings/contact-info-section

This command will generate two files:
1. templates/components/contact-info-section.hbs
2. components/contact-info-section.js

Following is the markup in contact-info-section.hbs:

<form class="ui form" {{action 'submit' on='submit'}} novalidate>
  <div class="field">
    <label>{{t 'Email'}}</label>
    {{input type='email' name='email' value=email}}
  </div>
  <div class="field">
    <label>{{t 'Phone'}}</label>
    {{input type='text' name='phone' value=phone}}
  </div>
  <button class="ui teal button" type="submit">{{t 'Save'}}</button>
</form>

In the form, we are having two fields for inputs ‘Email’ and ‘Phone’ respectively. We are using Ember input helpers so as to achieve easy data binding. The name is used as an identifier for the validation.
We have one submit button at the bottom which saves the information that the user wants to update.

The contact-info-section.js file contains all the validation rules which validate the form when the user submits it. If a user enters any field empty, the form prompts the user with a message. Following is an example of the validation rules for the email field:

email: {
          identifier : 'email',
          rules      : [
            {
              type   : 'empty',
              prompt : this.l10n.t('Please enter your email ID')
            },
            {
              type   : 'email',
              prompt : this.l10n.t('Please enter a valid email ID')
            }
          ]
        }

Similar rules are there for the ‘phone’ field input.

2) contact-info.js
The contact-info.js renders our route. Thus it contains the ‘titletoken’ method which returns the ‘titletoken’ method which returns the page title.

Thus, after doing all the things above, we get the following as a result.

Resources:

Source code: https://github.com/fossasia/open-event-frontend

Maintaining Aspect Ratio of Images while Uploading in Open Event Frontend

In Open Event Frontend, we are using the image-upload at many places such as the cover photo of the event on the create event page, also at the system images (in the admin routes) where the user gets to change the image uploaded by him earlier, and also at the ‘profile’ route where the user can change his photo. But at different places, different dimensions of photos are needed since we are keeping standard in Open Event Frontend.

Therefore the image needs to be in a size with the correct ratio at the time of uploading. We are using the cropper component  for achieving this. The cropper is a modal which pops up after the image upload modal so that a user can crop the image according to the aspect ratio kept specific for that purpose. It looks as follows:

While dealing with an issue in Open Event Frontend, we had to have change in aspect ratio for ‘avatar’ images, unlike the other images. So, we had to modify our cropper modal so as to have different aspect ratios for different images when needed.

We solved the above problem as follows:

In our image-modal, we pass the aspect ratio as a parameter. So in our case, we wanted to set the aspect ratio 1:1 for the ‘avatar’ images only. Following is the code snippet of what we wanted:

{{widgets/forms/image-upload
needsCropper=true
label=(t 'Update Image')
id='user_image'
aspectRatio=(if (eq subTopic.name 'avatar') (array 1 1))
icon='photo'
hint=(t 'Select Image')
maxSizeInKb=10000
helpText=(t 'For Cover Photos : 300x150px (2:1 ratio) image.
For Avatar Photos : 150x150px (1:1 ratio) image.')}}

Thus, we passed the ‘aspectRatio’ as a parameter to the ‘image-upload’ modal. The image-upload further calls the cropper modal and passes ‘aspectRatio’.

{{#if needsCropper}}
{{modals/cropper-modal isOpen=cropperModalIsShown imgData=imgData onImageCrop=(action 'imageCropped') aspectRatio=aspectRatio}}
{{/if}}

Thus, we can use the passed param i.e ‘aspectRatio’ in our cropper to modify the logic for the aspect ratio. Following is what we did so as to obtain the aspect Ratio of 1:1 for ‘avatar’ images only. The default aspect ratio for all other images is 2:1.

onVisible() {
let viewPort = {};
let factor = 150;
const aspectRatio = this.getWithDefault('aspectRatio', [2, 1]);
viewPort.width = aspectRatio[0] * factor;
viewPort.height = aspectRatio[1] * factor;
viewPort.type = 'square';
this.$('.content').css('height', '300px');
this.$('img').croppie({
customClass : 'croppie',
viewport : viewPort,
boundary : {
height: 250
}
});
},

As shown above, we have kept a multiplying factor which is fixed. According to the aspect ratio specified, we calculate and set the width and height in the viewport object.

Thus, following is the thing (Aspect Ratio 1:1 ) we achieved for the ‘avatar’ images:


Resources
Official Ember JS guide: https://guides.emberjs.com/v1.10.0/templates/actions/

Blog on making our own modals: http://ember.guru/2014/master-your-modals-in-ember-js

Source code: https://github.com/sumedh123/open-event-frontend/tree/system-images

Creating Custom Validation Rules in Open Event Frontend

In the Open Event Frontend, users can create ‘Access codes’ for an event. Creation of access codes has a form located at the ‘tickets/access-codes/create’ which contains multiple form inputs such as ‘Number of Access tickets’, ‘status’, ‘Minimum tickets’, ‘Maximum tickets’, etc. To create an access code, a user has to fill the form. The form is validated at the time of submission. However, it should not happen that sometimes a user fills minimum number of tickets greater than maximum number of tickets. Semantic UI provides inbuilt validation for many inputs like the ‘text’, ‘email’, ‘password’, ‘number’ etc. But in some cases, it may happen that we need custom validation for some inputs before submitting the form.

         Number of tickets

             Min and max tickets

As shown in the above screenshot, the access code form has some inputs like ‘Min’, ‘Max’, ‘Number of access tickets’, etc. Here we need to ensure that the minimum number of tickets should not exceed the maximum number of tickets, also, the maximum number should not be greater than the total tickets, etc. To achieve this, we need to use the custom validation provided by Semantic UI.

To create a custom validation, we need to have a custom rule for it. To ensure the minimum value does not exceed the maximum value of the ticket, we first set up a rule for validation as follows:

min: {
identifier : 'min',
optional : true,
rules : [
{
type : 'number',
prompt : this.l10n.t('Please enter the proper number')
},
{
type : 'checkMaxMin',
prompt : this.l10n.t('Minimum value should not be greater than maximum')
}
]
},

In the above snippet, we introduced a new rule in the ‘min’ validation set of rules. The new rule called ‘checkMaxMin’ has to be defined so that it checks the necessary condition and returns the value desired.

$.fn.form.settings.rules.checkMaxMin = () => {
if (this.get('data.minQuantity') > this.get('data.maxQuantity')) {
return false;
}
return true;
};

 

Above code shows the rule which we defined in to check the required condition. It retrieves the data from the form by ‘this.get’ method and then checks whether the maximum tickets exceed the minimum. If they do, the function returns false, else it returns true. In this way, when a true value is returned, the form accepts the inputs else it rejects. The following screenshots illustrate the same:

In the same way, we can write custom validation for checking whether maximum tickets exceed the total number of tickets. Following is the code snippet and screenshot for the same.

max: {
identifier : 'max',
optional : true,
rules : [
{
type : 'number',
prompt : this.l10n.t('Please enter the proper number')
},
{
type : 'checkMaxMin',
prompt : this.l10n.t('Maximum value should not be less than minimum')
},
{
type : 'checkMaxTotal',
prompt : this.l10n.t('Maximum value should not be greater than number of tickets')
}
]
},

 

$.fn.form.settings.rules.checkMaxTotal = () => {
if (this.get('data.maxQuantity') > this.get('ticketsNumber')) {
return false;
}
return true;
};

Thus, in this way, we can use custom validation for some form inputs when we want to customise the inputs based on our requirements.

Resources:

Rendering Child Route Templates Independent of Parent Template in Open Event Frontend

In the Open Event Frontend, we are having routes and it’s child routes so as to render our content on the webpage. Child routes are needed when we want to have multiple routes under a single route as a subroute. In Ember js, the child routes of a route inherit the parent content in them by default . But, in some cases, we may not want the parent template’s content to get rendered into the child route and may want the child template to be entirely different. This issue deals with the same that how can we achieve the child template entirely different from the parent one.

                                                  events/tickets/index

                                                   events/tickets/attendees

                                                   events/tickets/orders

Above screenshots show the ‘events/tickets’ and it’s child routes. Since the ‘tickets’ is the parent and it contains the left side menu as it’s content which is common in all the child routes shown (orders, attendees, index). The content on the right of the side menu is the child’s own content. Now let’s take the case of ‘tickets/access-codes’ route which has a child route ‘tickets/access-codes/create’ where we do not want to have the parent content in it.

Following screenshots show the above thing that we want to replicate:

                                                 events/tickets/access-codes

This is the parent template (in the issue) whose child template had to be entirely different which is shown as follows:

                                                events/tickets/access-codes/create

By default, the content of the parent template i.e ‘tickets/access-codes’ would have been rendered in create.hbs which is it’s child as shown above.

To avoid this behavior, I used the current route path which helps to customize the contents to be rendered in the child. For this I implemented a check which matches the currentRouteName with the one which we want to render (child). If it does not match, then the content of the parent will be rendered along with the child, otherwise, a separate template (only child) without the parent’s content will be rendered. Following code illustrates the same:

{{#if (not-includes session.currentRouteName 'events.view.tickets.access-codes.create')}}
<!-- Things you want to show with the parent content in it.-->
{{else}}
<!-- Things you don't want parent to be in.-->
 {{outlet}}
{{/if}}

 

Conclusion:

In this way we can customize whether to include the parent’s content in child routes or completely render different child route independent of parent’s content in it.

Resources:

Using Getter and Setter to set Properties and Manipulate Responses to Templates in Ember JS on Open Event Front-end

In the Open Event Front-end our aim is to make it work on all devices regardless of screen size. We need the UI to be responsive. One feature of the system is that users can change system images. The idea in this issue was to enable users to select a ‘topic’ from the left side menu that would trigger an action which dynamically changes the right part of the page. For example, we could show ‘subTopics’ and their respective images based on the data given in the dictionary in Open Event Frontend project. Open Event Frontend has some static data files which are called dictionaries. They contain Javascript Objects which are used throughout the project. The solution I am implementing here is to get the data from the template, set it as a property of the Ember object with the help of setter, and use it again with the help of getter to manipulate our response to the template. Thus in a controller, we can perform manipulation of the data which we want to return to the template.

In Open Event Front-end we have a dictionary (event.js) which we are using to load our topics into the left side menu.

 

export const eventTopics = {
'Auto, Boat & Air': [
'Air', 'Auto', 'Boat', 'Motorcycle/ATV', 'Other'
],
'Business & Professional': [
'Career', 'Design', 'Educators', 'Environment & Sustainability',
'Finance', 'Media', 'Non Profit & NGOs', 'Other', 'Real Estate',
'Sales & Marketing', 'Startups & Small Business'
],
'Charity & Causes': [
'Animal Welfare', 'Disaster Relief', 'Education',
'Environment', 'Healthcare', 'Human Rights',
'International Aid', 'Other', 'Poverty'
],
'Community & Culture': [
'City/Town', 'County', 'Heritage', 'LGBT', 'Language',
'Medieval', 'Nationality', 'Other', 'Renaissance', 'State'
]
};

                                                           event.js

To achieve this, on the click action of the topic in the left side menu, we have to somehow remember the topic and return the subtopics according to it from the dictionary dynamically.

Following this approach: A controller is needed to remember the topic selected from the left side rail. We set the properties on the controller to achieve this thing. This is where the ‘Getters’ and ‘Setters’ come to help.

 

import Ember from 'ember';
import { eventTopics } from 'open-event-frontend/utils/dictionary/event';
import { keys, uniqBy } from 'lodash';

const { Controller, computed } = Ember;

export default Controller.extend({
imageTopic: keys(eventTopics)[0],

topics: computed(function() {
return keys(eventTopics);
}),

imageObjects: computed('imageTopic', 'model', function() {
let subTopics = eventTopics[this.get('imageTopic')];
let filteredImageArray = this.get('model').filter(function(obj) {
return (subTopics.includes(obj.name));
});
filteredImageArray = uniqBy(filteredImageArray, 'name');
return filteredImageArray;
}),

actions: {
setImagePart(imageTopic) {
this.set('imageTopic', imageTopic);
},
openModal() {
this.set('isModalOpen', true);
}
}
});

                                             system-images.js(controller)

 

{{#tabbed-navigation isVertical=true}}
{{#each topics as |topic|}}
<a href="#" class="link item {{if (eq imageTopic topic) 'active'}}" {{action 'setImagePart' topic}}>{{topic}}</a>
{{/each}}
{{/tabbed-navigation}}

                                          system-images.hbs(template)

When a link from the left side menu is clicked, an action named “setImagePart” is triggered and the controller listens to the action. Here, we are passing a parameter called ‘topic’ from the template and getting it in the controller as ‘imageTopic’. The next part of the action is to set the property of the object Controller i.e the “setImagePart” action sets the imageTopic (in this case overrides) property and sets it to the selected one. Thus, we now have the controller with the data selected from the left side menu saved in a property called imageTopic.

Now as we can see we have imported the eventTopics object from the dictionary.

Next task is to get the respected subtopics present in the dictionary. Now since we have set the ‘imageTopic’ property, we can access it to get the subtopics simply by getter “this.get(‘imageTopic’)”. Thus the subTopics (values of the eventTopics object) are returned.

Resources: https://guides.emberjs.com/v1.10.0/object-model/computed-properties/

Choosing Semantic UI over Bootstrap for the Open Event Front-end

There are many design frameworks. In this blogpost, I am going to list out some pros of Semantic UI and why it is better suited for the Open Event project than Bootstrap or other frameworks.

Components:

The very main reason why we chose Semantic UI in Open Event frontend is the number of components it offers, such as step, segment, rails, cards, lists, headers, dimmers, dropdown, and form validation. We can use many of these components right away, which helps us to speed up development.

Installation:

Semantic UI also provides very good integrations with the majority of technologies available out there. Bootstrap, however, can be integrated anywhere with CDN’s.

Following are the technologies that Semantic UI can be integrated with:

  • As a NPM package
  • React
  • Angular
  • Ember JS

Also Semantic UI has CDN’s. Thus Semantic UI has easeful installation. The documentation on the official site provides a good view of installation.

Documentation:  

In regards to the documentation too, Semantic UI is more suitable for us. The official site has a neat and clean documentation for each component. In the case of Bootstrap, the documentation is also good but personally, for me, the Semantic UI’s documentation is easier to read understand.

Designed with completely em:

em is a unit in CSS which ensures that components are scaled according to the different device sizes.Every component is made by using em and rem so that the webpage doesn’t get distorted whenever screen size changes.

Concise and Expressive:  

In English, it’s much easier to say “There are three tall men” than “There is a tall man, a tall man, and a tall man”. This means that in Bootstrap, to achieve a thing (say a navbar), we need to have 2-3 nested classes of “navbar”, ”navbar-default”, etc. whereas with Semantic UI, it can be done with single one.

Code appearance:

In the case of Semantic UI, the code looks much cleaner since the classes created are of a short length and cause less confusion. Following code indicates how clean things can be done with Semantic UI.

class="ui secondary menu"> class="active item"> Home class="item"> Messages class="item"> Friends
class="right menu">
class="item">
class="ui icon input"> type="text" placeholder="Search..."> class="search link icon">
</div> <a class="ui item"> Logout </a> </div> </div>

                    Fig. Semantic UI

<nav class="navbar navbar-default">
class="container-fluid">
class="navbar-header"> class="navbar-brand" href="#">Brand
<!-- Collect the nav links, forms, and other content for toggling -->
class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
class="navbar-form navbar-left">
class="form-group"> type="text" class="form-control" placeholder="Search">
<button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav>

                      Fig. Bootstrap

Looks:

In terms of looks, it is well said that “User makes user experience” i.e the user experience depends more on the user viewing the website rather than the developer. Still, according to me, every Bootstrap site looks similar whereas a site built with Semantic provides a different, crisp look. Unlike other sites, a site built with Semantic looks beautiful.

Redesigning infinitely:

Creating a site in Semantic means you never have to rewrite your codebase from scratch. Redesigning means retooling your UI toolkit, adjusting UI definitions, not creating entirely new HTML layouts.

Conclusion:

There are many reasons why Semantic UI is a bit more suitable, powerful than Bootstrap for the Open Event project. Still, the choice of the framework depends on the purpose of your website or what you want to build. The official documentation of Semantic-UI can be found out at https://semantic-ui.com.