Customizing Serializers in Open Event Front-end

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

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

Creating a serializer for model

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

ember g serializer user

 
Customizing serializer

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

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

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

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

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

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

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

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

Learn more about how to customize serializers in ember data here

Semantic-UI Validations for Forms in Open Event Frontend

Open Event Frontend requires forms at several places like at the time of login, for creation of events, taking the details of the user, creating discount codes for tickets etc.. Validations for these forms is a must, like in the above picture, we can see that many fields like discount code, amount etc. have been left empty, these null values when stored at backend can induce errors.

Semantic-UI makes our life easier and provides us with it’s own validations. Its form validation behavior checks data against a set of criteria or rules before passing it along to the server.

Let’s now dive deeper into Semantic validations, we’ll take discount code form as our example. The discount code form has many input fields and we have put checks at all of them, these checks are called rules here. We’ll discuss all the rules used in this form one by one

  1. Empty

Here we check if the input box with the identifier discount_amount is empty or not, if it is empty, a prompt is shown with the given message.

         identifier : ‘discount_amount’,
         rules      : [
           {
             type   : ’empty’,
             prompt : this.l10n.t(‘Please enter the discount amount’)
           }
         ]

2. Checked
Here, we validate whether the checkbox is checked or not and if it is not, show corresponding message

rules      : [
   {
     type   : ‘checked’,
     prompt : this.l10n.t(‘Please select the appropriate choices’)
   }]

3. RegExp

These checks are very important in input fields requiring passwords and codes, they specify the allowed input characters

rules      : [{
   type  : ‘regExp’,
   value : ‘^[a-zA-Z0-9_-]*$’
}]

4.Custom rules

Many a times, we require some rules which are by default not given by semantic, here we can create custom rules.

Like here, we want to check whether the user has not set max value lower than min.

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

Here, we are creating our own custom rule checkMaxMin which returns boolean value depending upon minQuantity and maxQuantity. Now, this can be directly used as a rule

identifier : ‘min_order’,
optional   : true,
rules      : [
 {
  type   : ‘checkMaxMin’,
  prompt : this.l10n.t(‘Minimum value should not be greater than maximum’)
 }]

You can find the above code here

Additional Resources

Create Discount Code Component in Open-Event-Frontend

We in Open-Event-Frontend have given the event organiser the feature to create discount coupons for his or her event. Here the organiser can either enter the discount amount or discount percentage and can set even set the total number of coupons he wants to make available for his customers. We have also automatically generated an unique link for each discount coupon.

We’ll be creating a separate component create-discount-code for creating discount codes.To create the component we’ll run the following command

ember g component forms/events/view/create-discount-code

This will create

1.Create-discount-code.hbs

Here we have designed our form.We have nested all the fields used, inside semantic’s ui form class.Some of the helpers used in the form are

We have used the ember input helper in following way for all the input fields.The

attribute name,value corresponds to the id and value attached with the helper

{{input type=‘text’ name=‘discount_code’ value=data.code}}

Ember radio buttons are used by the organizer to select between discount

{{ui-radio label=(t ‘Amount (US$)’)
          name=‘discount_type’  
          value=‘amount’
          current=‘amount’
          onChange=(action (mut selectedMode))}}

 

We have given the organizer an additional option to set the validity of the discount code. For this we have used date-picker and time-picker component already present in Open-Event-Frontend in the following manner.

<div class=“fields”>
       <div class=“wide field {{if device.isMobile ‘sixteen’ ‘five’}}”>
         <label>{{t ‘Valid from’}}</label>
         {{widgets/forms/date-picker id=’start_date’ value=data.validFromDate rangePosition=’start’}}
         <div class=“ui hidden divider”></div>
         {{widgets/forms/time-picker id=’start_time’ value=data.validFromTime rangePosition=’start’}}
       </div>
       <div class=“wide field {{if device.isMobile ‘sixteen’ ‘five’}}”>
         <label>{{t ‘Expires on’}}</label>
         {{widgets/forms/date-picker id=‘end_date’ value=data.validTillDate rangePosition=‘end’}}
         <div class=“ui hidden divider”></div>
         {{widgets/forms/time-picker id=‘end_time’ value=data.validTillTime rangePosition=‘end’}}
       </div>
     </div>

The above snippet will the following output

2.Create-discount-code.js

Here we validate the form and provide it with an unique discount code url. We have generated the url using the event id and the discount code.

discountLink: computed(‘data.code’, function() {
 const params = this.get(‘routing.router.router.state.params’);
 return location.origin + this.get(‘routing.router’)
                         .generate(‘public’, params[‘events.view’]                          .event_id,
        { queryParams: { discount_code: this.get(‘data.code’) } });
}),
actions: {
 submit() {
   this.onValid(() => {
   });
 }
}

3.Create-discount-code-test.js

This is where we check whether our component is compatible with other components of the system or not. Here, for now, we are just making sure if our component renders or not, by checking the presence of ‘Save’.

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

moduleForComponent(‘forms/events/view/create-discount-code’, ‘Integration | Component | forms/events/view/create discount code’);

test(‘it renders’, function(assert) {
 this.render(hbs`{{forms/events/view/create-discount-code routing=routing}}`);
 assert.ok(this.$().html().trim().includes(‘Save’));
});

Now, our component is ready, and the only part remaining is to place it in our application. We place it in app/templates/events/view/tickets/discount-codes/create.hbs in the given form.

{{forms/events/view/create-discount-code data=model}}

Here we have passed model from create-discount-code.js to data used in Create-discount-code.hbs

Now our create discount code page is up and running

Additional Resources

Adding Messaging Route in Ember.js for Admin UI of Open Event Frontend

In this blog post I am explaining how we implement a messages page for admins to keep track of all types of system messages sent to users in the Open Event Frontend. The page shows the types of messages sent out to various users at one place and as well as additional details. It offers configuration options to control which messages get sent out  as emails or notifications or both. And, the page shows when and what message should be sent via notification or mail.
To create the messages page we’ll run the following command

ember generate route admin/messages

This will create

This command will also add  this.route(‘messages’);  to router.js. As admin is the parent route for messages, messages will be nested inside admin in router.js

this.route(‘admin’, function(){
  this.route(‘messages’);
});

Let’s now understand the content of each of above files.

  1. Messages.js

In admin/messages.js we have used titletoken helper to set the title of the tab. Here we have created the message model and added attribute like recipient, trigger, emailMessage, notificationMessage, options and sentAt. We have returned this model from the js file to template.

import Ember from ’ember’;
const { Route } = Ember;
export default Route.extend({
 titleToken() {
   return this.l10n.t(‘Messages’);
 },
 model() {
   return [{
     recipient: [
       {
         name: ‘Organizer’
       },
       {
         name: ‘Speaker’
       }
     ],
     trigger      : ‘Title1’,
     emailMessage : {
       subject : ‘Email subject1’,
       message : ‘Hi, the schedule for session1 has been changed’
     },
     notificationMessage: {
       subject : ‘Notification subject1’,
       message : ‘Hi, the schedule for session1 has been changed’
     },
     option: {
       mail         : true,
       notification : false,
       userControl  : true
     },
     sentAt: new Date()
   }];
 }
});

 

  1. Messages.hbs

In template we have created a table and added classes like stackable and compact. Stackable class makes the table responsive and stacks all the rows for devices with smaller screen size. Compact class helps to show more number of rows at a time.

Then in the template we iterate through the model using a loop. Here we have used other semantic-ui elements like ui ordered list , ui header, ui-checkbox inside the table. For options column we have three attributes denoting how the admin wants to send the message to the user. Here we have grouped three fields using the class grouped fields .In each field we have used semantic’s  ui-checkbox .In check-box we are mutating values on click by using mut helper.

<div class=“grouped fields”>
 <div class=“field”>
   {{ui-checkbox checked=message.option.mail
                 label=(t ‘Mail’)      
                 onChange=(action (mut message.option.mail))}}
 </div>
 <div class=“field”>
   {{ui-checkbox checked=message.option.notification
                 label=(t ‘Notification’)  
               onChange=(action (mut message.option.notification))}}
 </div>

 <div class=“field”>
   {{ui-checkbox checked=message.option.userControl
                label=(t ‘User Control’)  
               onChange=(action (mut message.option.userControl))}}
 </div>
</div>

We are getting date object from js and to convert it into human readable format we have used moment like {{moment-format message.sentAt ‘dddd, DD MMMM YYYY’}}

  1. Messages-test.js
import { test } from ’ember-qunit’;
import moduleFor from ‘open-event-frontend/tests/helpers/unit-helper’;

moduleFor(‘route:admin/messages’, ‘Unit | Route | admin/messages’, []);

test(‘it exists’, function(assert) {
 let route = this.subject();
 assert.ok(route);
});

Using this we can test the existence of our route. These tests are run using the command ember t.

Our message page is ready now. The admin can have a check at all the messages sent to users.

Additional Resources