Stubbed Routing Inbuilt Service used in Open Event Frontend

In Open Event Frontend, we have used services like ‘auth-manager’, ‘l10n’, ‘loader’, ‘sanitizer’, etc to ease our work with the help of predefined-functions in those services. However, while dealing with an issue in the project, there was a need to use ‘Routing’ as a service.

In the issue, we wanted to generate an access link dynamically from the access code entered by the user. The format of the access link was as follows:

“base_url + event_id + access_code”

So, for the above URL, we needed to have ‘event_id’ and ‘access_code’.

The ‘access_code’ can be readily accessed from the user’s input itself, whereas to get the event_id, we used the ‘Routing’ service in Ember.

Generally to use a service in Ember, it has to be written first,then registered, injected and then used.

‘Routing’ service in Ember is an inbuilt service unlike the ones listed at the beginning.

There is no need to write it. It can be simply registered, injected and used.

this.register('service:routing', routingStub);
this.inject.service('routing', { as: 'routing' });

where ‘register’ and ‘inject’ are the methods on Ember objects.

The integration tests in Open Event Frontend are written such that the services can be used without injecting, but the tests will fail. To pass those tests, we had to register and inject the service in the required component.

The Routing service could thus be registered and injected into the specific component( injection in the component’s integration test ) only but for future needs, this service might be needed in any other component too. For this purpose, this service was registered and injected in ‘component-helper.js’.

const routingStub = Service.extend({
  router: {
    router: {
      state: {
        params: {
          'events.view': {
            event_id: 1
          }
        }
      }
    },
    generate() {
      return 'http://dummy-url.com';
    }
  }
});


export default function(path, name, testCase = null) {
  moduleForComponent(path, name, {
    integration: true,

    beforeEach() {
      this.register('service:routing', routingStub);
      this.inject.service('routing', { as: 'routing' });
      this.register('service:l10n', L10n);
      this.inject.service('l10n', { as: 'l10n' });
      this.application = startApp();
      l10nTestHelper(this);
      run(() => fragmentTransformInitializer.initialize(getOwner(this)));
    }
  }
}

Stubbing a Service: This is a process of faking an app of importing a service when no path is available to import. Stubbing of a service is mainly done when one needs to deal with the testing of the app. In our case, the same is done. We have stubbed the ‘Routing’ service in order to deal with the testing part. It can be seen from the above code that we have generated a ‘routingStub’ which fakes the app while registering the service in the ‘beforeModel’. The next line of code shows the ‘injection’ of service into the app.

Now we are just left with one task i.e to pass ‘routing’ from our integration tests to the component.

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

Above code shows the same.

Thus we can stub the services in Ember when any component depends on them.

Resources:

Official Ember guide: https://guides.emberjs.com/v2.1.0/testing/testing-components

Blog by Todd Jordan: http://presentationtier.com/stubbing-services-in-emberjs-integration-tests/

Source codehttps://github.com/sumedh123/open-event-frontend/blob/0b193ca679ce3b51f65e19ee0d03ac6a679258de/tests/helpers/component-helper.js

Continue Reading

Creating System Images UI in Open Event Frontend

In Open Event Frontend, under the ‘admin/content’ route, ‘system-images’ route is present in which a user can update the image of the event topic he has uploaded at the time of creating an event. We achieved this as follows:

First, we create a route called ‘system/images’.

ember g route admin/content/system-images

This will generate three files:
1) routes/admin/content/system-images.js (route)
2) templates/admin/content/system-images.hbs (template)
3) test/unit/routes/admin/content/system-images-test.js (test file)
We also create a subroute of system-images route so as to render the subtopics queried through API.

ember g route admin/content/system-images/list

This will generate three files:
1) routes/admin/content/system-images/list.js(subroute)
2) templates/admin/content/system-images/list.hbs(template)
3) test/unit/routes/admin/content/system-imageslist-test.js(test file)

From our ‘system-images’ route, we render the ‘system-images’ template. We have a subroute of system-images route called as ‘list’ in which we render the subtopics available to us via API. The left side menu is the content of ‘system-images.hbs’ and the content on the right is it’s subroute i.e ‘list.hbs’. The ‘list’ subroute provides a facility to upload the system image. The API returns an array of objects containing subtopics as follows(single object is shown here, there will be multiple in the array)

{
            id          : 4545,
            name        : 'avatar',
            placeholder : {
              originalImageUrl : 'https://placeimg.com/360/360/any',
              copyright        : 'All rights reserved',
              origin           : 'Google Images'
            }
          },

Following is the content of our uploader i.e ‘list.hbs’ which is a subroute of the system-images.hbs.

<div class="ui segment">
  {{#each model as |subTopic|}}
    <h4>{{subTopic.name}}</h4>
    <img src="{{subTopic.placeholder.originalImageUrl}}" class="ui fluid image" alt={{subTopic.name}}>
    <div class="ui hidden divider"></div>
    <button class="ui button primary" {{action 'openModal' subTopic}} id="changebutton">{{t 'Change'}}</button>
  {{/each}}
</div>
{{modals/change-image-modal isOpen=isModalOpen subTopic=selectedSubTopic}}

We can see from the above template that we are iterating the response(subtopics) from the API. For now, we are just using the mock server response since we don’t have API ready for it. There is one ‘upload’ button which opens up the ‘change-image-modal’ to upload the image which looks as follows:

The ‘change-image-modal.hbs’ has a content as follows:

<div class="sixteen wide column">
        {{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.')}}

        <form class="ui form">
          <div class="field">
            <label class="ui label">{{t 'Copyright information'}}</label>
            <div class="ui input">
              {{input type="text"}}
            </div>
          </div>
          <div class="field">
            <label class="ui label">{{t 'Origin information'}}</label>
            <div class="ui input">
              {{input type="text"}}
            </div>
          </div>
        </form>

      </div>

The above uploader has a custom ‘image-upload’ widget which we are using throughout the Open Event Frontend. Also, there are two input fields i.e ‘copyright’ and ‘origin’ information of the image. On clicking the ‘Select Image’ button and after selecting our image from the file input, we get a cropper for the image to be uploaded. The image can be cropped there according to the aspect ration maintained for it. The cropper looks like:

Thus, a user can update the image of the Event Topic that he created.

Resources:

Ember JS Official guide.

Mastering modals in Ember JS by Ember Guru.

Source codehttps://github.com/fossasia/open-event-frontend

 

Continue Reading

Implementing JSON API for ‘settings/contact-info’ route in Open Event Frontend

In Open Event Frontend, under the settings route, there is a ‘contact-info’ route which allows the user to change his info (email and contact). Previously to achieve this we were using the mock response from the server. But since we have the JSON API now we could integrate and use the JSON API for it so as to let the user modify his/her email and contact info. In the following section, I will explain how it is built:

The first thing to do is to create the model for the user so as to have a skeleton of the database and include our required fields in it. The user model looks like:

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'),
  details   : attr('string'),
  contact   : attr('string'),
});

Above is the user’s model, however just the related fields are included here(there are more fields in user’s model). The above code shows that email and contact are two attributes which will accept ‘string’ values as inputs. We have a contact-info form located at ‘settings/contact-info’ route. It has two input fields as follows:

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

The form has a submit button which triggers the submit action. We redirect the submit action from the component to the controller so as to maintain ‘Data down, actions up’. The code is irrelevant, hence not shown here. Following is the action which is used to update the user which we are handling in the contact-info.js controller.

updateContactInfo() {
      this.set('isLoading', true);
      let currentUser = this.get('model');
      currentUser.save({
        adapterOptions: {
          updateMode: 'contact-info'
        }
      })
        .then(user => {
          this.set('isLoading', false);
          let userData = user.serialize(false).data.attributes;
          userData.id = user.get('id');
          this.get('authManager').set('currentUserModel', user);
          this.get('session').set('data.currentUserFallback', userData);
          this.get('notify', 'Updated information successfully');
        })
        .catch(() => {
        });
    }

We are returning the current user’s model from the route’s model method and storing it into ‘currentUser’ variable. Since we have data binding ember inputs in our contact-info form, the values will be grabbed automatically once the form submits. Thus, we can call ‘save’ method on the ‘currentUser’ model and pass an object called ‘adapterOptions’ which has key ‘updateMode’. We send this key to the ‘user’ serializer so that it picks only the attributes to be updated and omits the other ones. We have customized our user serializer as:

if (snapshot.adapterOptions && snapshot.adapterOptions.updateMode === 'contact-info') {
        json.data.attributes = pick(json.data.attributes, ['email', 'contact']);
}

The ‘save’ method on the ‘currentUser’ ‘currentUser.save()’ returns a promise. We resolve the promise by setting the ‘currentUserModel’ as the updated ‘user’ as returned by the promise. Thus, we are able to update the email and contact-info using the JSON API.

Resources:

Ember data official guide

Blog on models and Ember data by Embedly

Continue Reading

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

Continue Reading

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

Continue Reading

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

Continue Reading

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:

Continue Reading

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:

Continue Reading

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/

Continue Reading

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.

Continue Reading
  • 1
  • 2
Close Menu