Create new route ‘/settings’ to display project’s configuration

Having a separate route to display all the hardcoded project’s configuration makes it very easy to compare web apps with each other. A similar architecture has been followed in loklak to create a ‘/settings’ route to display configs used inside it. Respective implementation would be discussed in this blog.

Creating Settings Component

First step would be to create a Settings component to render under /settings route.

ng g component settings

 

This command will create new settings component. We have to create settings-routing and settings module inside settings component separately. This route module will be called inside the root app module. The settings-routing module would import Router module as follows:

import { NgModule } from ‘@angular/core’;
import { Routes, RouterModule } from ‘@angular/router’;
import { SettingsComponent } from ‘./settings.component’;
const routes: Routes = [
    {
        path: '',
        component: SettingsComponent
    }
];
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule],
    providers: []
})
export class LoklakSettingsRoutingModule { }

 

The settings module would follow the basic module configuration of Angular, and import the settings-routing module as follows:

import { NgModule } from ‘@angular/core’;
import { CommonModule } from ‘@angular/common’;
import { LoklakSettingsRoutingModule }
    from ‘./settings-routing.module’;
import { SettingsComponent } from ‘./settings.component’;
@NgModule({
    imports: [
        CommonModule,
        LoklakSettingsRoutingModule
    ],
    declarations: [
        SettingsComponent
    ]
})
export class SettingsModule { }

 

At this point of time, we are having a basic settings component to render under /settings route.

Loading /settings as child route under root app module

Now, we need to add path of ‘/settings’ route inside root app module as follows:

path: ‘settings’,
loadChildren: ‘./settings/settings.module#SettingsModule’

 

Which means when user is on path /settings, load settings component.

Fetching project configs inside settings component

At this point, we have successfully established /settings route and settings component. Now we need to fetch the project configs inside the settings component to display. We would actually import the config and store it in an array to loop through it in template file using *ngFor to display the items under config array.

Note: We would import newsOrgs containing twitter username of news organizations, and the defaultUrlConfig containing the list of all hardcoded URLs used inside the project. It can be taken as an example to add and display more configurations.

import { Component, OnInit } from ‘@angular/core’;
import { defaultUrlConfig } from ‘../shared/url-config’;
import { newsOrgs } from ‘../shared/news-org’;
@Component({
    selector: ‘app-settings’,
    templateUrl: ‘./settings.component.html’,
    styleUrls: [‘./settings.component.scss’]
})
export class SettingsComponent implements OnInit {
    public urlObject = [];
    public newsConfigOrgs = newsOrgs;
    ngOnInit() {
    let i = 0;
    for (const key in defaultUrlConfig) {
        if (key) {
            const data = [];
            const value = defaultUrlConfig[key];
            const data2 = [];
            for (const key2 in value) {
                if (key2) {
                    const value2 = value[key2];
                    data2.push(key2, value2);
                }
            }
            data.push(key, data2);
            this.urlObject.push(data);
            i++;
            }
        }
    }
}

 

urlObject is an array used to store the list of hardcoded URLs, and newsConfigOrgs is an array containing all the list of news organizations.

Displaying array items in template file

Up to this point, we have successfully stored the data inside the component file of settings component and now we need to display this data in template file. For a sake of an example, we would display news orgs as follows:

<!– News Config –>
<h1><u>News Configuration</u></h1>
<div class=“url-container” *ngFor=“let key of newsConfigOrgs”>
   <h4 class=“news”>{{key}}</h4>
</div>

The above code could be used as an example to display more config stored inside component file. Only the basic implementation has been followed and mentioned inside this blog, complete code can be found here: code.

Creating settings tab in Advanced-Feed

Now we need to provide a link to user to check /settings route. We would actually add another tab as settings inside Advanced-Feed as follows:

<a routerLink=“/settings” target=“_blank” class=“tab”>
   Settings
</a>

Testing /settings route

Search a query on loklak.org, and then click on settings tab which would be similar to:

On clicking settings tab, user should be directed to the /settings route containing non-editable (read-only) configuration.

Resources

Adding Speech Component in Loklak Search

Speech recognition service for voice search is already embedded in Loklak. Now the idea is to use this service and create a new separate component for voice recognition with an interactive and user friendly interface. This blog will cover every single portion of an Angular’s redux based component from writing actions and reducers to the use of created component in other required components.

Creating Action and Reducer Function

The main idea to create an Action is to control the flow of use of Speech Component in Loklak Search. The Speech Component will be called on and off based on this Action.

Here, the first step is to create speech.ts file in actions folder with the following code:

import { Action } from '@ngrx/store';

export const ActionTypes = {
   MODE_CHANGE: '[Speech] Change',
};

export class SearchAction implements Action {
   type = ActionTypes.MODE_CHANGE;

   constructor(public payload: any) {}
}

export type Actions
   = SearchAction;

 

In the above segment, only one action (MODE_CHANGE) has been created which is like a boolean value which is being returned as true or false i.e. whether the speech component is currently in use or not. This is a basic format to be followed in creating an Action which is being followed in Loklak Search. The next step would be to create speech.ts file in reducers folder with the following code:

import { Action } from '@ngrx/store';
import * as speech from '../actions/speech';
export const MODE_CHANGE = 'MODE_CHANGE';

export interface State {
   speechStatus: boolean;
}
export const initialState: State = {
   speechStatus: false
};
export function reducer(state: State = initialState,
   action: speech.Actions): State {
        switch (action.type) {
            case speech.ActionTypes.MODE_CHANGE: {
                const response = action.payload;
                return Object.assign({}, state,
                {speechStatus: response});
       }
       default: {
           return state;
       }
   }
}
export const getspeechStatus = (state: State) =>
    state.speechStatus;

 

It follows the format of reducer functions created in Loklak Search. Here, the main key point is the state creation and type of value it is storing i.e. State is containing a speechStatus of type boolean. Defining an initial state with speechStatus value false (Considering initially the Speech Component will not be in use). The reducer function a new state by toggling the input state based on the type of Action created above and it returns the input state by default. At last wrapping the state as a function and returning the state’s speechStatus value.

Third and last step in this section would be to create a selector for the above reducer function in the root reducer index file.

Import and add speech from speech reducer file into the general state in root reducer file. And at last export the created selector function for speech reducer.

import * as fromSpeech from './speech';
export interface State {
   ...
   speech: fromSpeech.State;
}
export const getSpeechState = (state: State) =>
    state.speech;
export const getspeechStatus = createSelector(
    getSpeechState, fromSpeech.getspeechStatus);

Creating Speech Component

Now comes the main part to create and define the functioning of Speech Component. For creating the basic Speech Component, following command is used:

ng generate component app/speech --module=app

 

It will automatically create and provide Speech Component in app.module.ts. The working structure of Speech Component has been followed as of Google’s voice recognition feature for voice searching. Rather than providing description of each single line of code the following portion will cover the main code responsible for the functioning of Speech Component.

Importing and defining speech service in constructor:

import {
    SpeechService
} from '../services/speech.service';
constructor(
       private speech: SpeechService,
       private store: Store<fromRoot.State>,
       private router: Router
   ) {
       this.resultspage =this.router.url
       .toString().includes('/search');
       if (this.resultspage) {
           this.shadowleft = '-103px';
           this.shadowtop = '-102px';
       }
       this.speechRecognition();
}
speechRecognition() {
    this.speech.record('en_US').subscribe(voice =>
        this.onquery(voice));
}

 

When the Speech Component is called, speechRecognition() method will start recording speech (It will use the record() method from speech service to record the user voice).

For fluctuating border height and color of voice search icon, a resettimer() method is created.

randomize(min, max) {
    let x;
    x = (Math.random() * (max - min) + min);
    return x;
}
resettimer(recheck: boolean = false) {
    this.subscription.unsubscribe();
    this.timer = Observable.timer(0, 100);
    this.subscription = this.timer.subscribe(t => {
    this.ticks = t;
        if (t % 10 === 0 && t <= 20) {
            this.buttoncolor = '#f44';
            this.miccolor = '#fff';
            this.borderheight =
                this.randomize(0.7, 1);
            if (this.resultspage) {
                this.borderheight =
                    this.randomize(0.35, 0.5);
            }
            if (!recheck) {
                this.resettimer(true);
            }
        }
        if (t === 20) {
            this.borderheight = 0;
        }
        if (t === 30) {
            this.subscription.unsubscribe();
            this.store.dispatch(new speechactions
            .SearchAction(false));
        }
    });
}

 

The randomize() method provides a random number between min and max value.

To put on check and display status as message on things like whether microphone is working, or user has spoken something, or if the speech is being recorded, based on the time elapsed in calling of speech component and actual voice recording, the following portion of code is written in ngOnInit() method.

ngOnInit() {
    this.timer = Observable.timer(1500, 2000);
    this.subscription = this.timer.subscribe(t => {
        this.ticks = t;
        if (t === 1) {
            this.message = 'Listening...';
        }
        if (t === 4) {
            this.message = 'Please check your 
            microphone and volume levels.';
            this.miccolor = '#C2C2C2';
        }
        if (t === 6) {
            this.subscription.unsubscribe();
            this.store.dispatch(new speechactions
                .SearchAction(false));
        }
    });
}

 

The logic can be understood as if the elapsed time is 1 sec, it means it is listening to the speaker’s voice. And if the elapsed time is 4 sec, it means there is something wrong and user will be asked to check for the microphone and volume levels. At last if it tends to 6 seconds, then the Speech Component will be called off with the dispatched Action as false which is defined above (That means it is no longer in use).

Embed Speech Component in main App Component

Now comes the last part to use the created featured component in the required place. Code below describes embedding Speech Component in App Component.

Import SpeechService and required modules.

import {
    SpeechService
} from './services/speech.service';
import { Observable } from 'rxjs/Observable';

 

hidespeech will be used to store the current status of Speech Component (whether its in use or not), and completeQuery$ and searchData store the voice recorded in form Observable and String. completeQuery$ is optional (If the Speech Component is unable to track voice of speaker by any means, then it will not contain any value and hence searchData will be empty).

hidespeech: Observable<any>;
completeQuery$: Observable<any>;
searchData: String;

 

Creating speech parameter in constructor and store the current status of speech and store it into hidespeech. Based on the subscribed value of hidespeech, speech service’s stoprecord() will be called (To stop recording when the speech recognition completes). After recording stops, store the whole query in completeQuery$.

constructor (
    private speech: SpeechService
) {
    this.hidespeech = store.select(
        fromRoot.getspeechStatus);
    this.hidespeech.subscribe(hidespeech => {
        if (!hidespeech) {
            this.speech.stoprecord();
        }
    });
    this.completeQuery$ = store.select(
        fromRoot.getQuery);
    this.completeQuery$.subscribe(data => {
        this.searchData = data;
    });
}

 

Add the Speech Component in app.component.html. Now the main logic of calling Speech Component will be based on the subscribed observable value of hidespeech (If false then call Speech Component else not).

<app-speech *ngIf="hidespeech|async"></app-speech>

Using Speech Component in Home and FeedHeader Component

Import Speech Service and speech Action created above, and create hidespeech to store the current status of Speech Component.

import * as speechactions from '../../actions/speech';
import {
    SpeechService
} from '../../services/speech.service';
hidespeech: Observable<boolean>;

 

Create speech parameter of type SpeechService and store the current status of Speech Component in hidespeech. Dispatch speechactions.SearchAction (payload as true) for inferring that the Speech Component is currently in use.

constructor(
    private speech: SpeechService
) {
    this.hidespeech = store
        .select(fromRoot.getspeechStatus);
}
speechRecognition() {
    this.store.dispatch(
        new speechactions.SearchAction(true));
}

How to use the Speech Component?

Goto Loklak and click on Voice Input Icon. It will popup a screen as below.

Now, speak something to search. E.g. Google, the screen will turn into something like below with the spelled value displayed on screen.

If something goes wrong (Microphone did not work, low volume levels or unrecognisable voice), then screen will show something like:

On successful recognition of speech, the query will be set and the results will be shown as

Similar process is being followed on results page to make a search query using voice.

Resources

Using Hidden Attribute for Angular in Susper

In Angular, we can use the hidden attribute, to hide and show different components of the page. This blog explains what the hidden attribute is, how it works and how to use it for some common tasks.
In Susper, we used the [hidden] attribute for two kinds of tasks.

  1. To hide components of the page until all the search results load.
  2. To hide components of the page, if they were meant to appear only in particular cases (say only the first page of the search results etc).

Let us now see how we apply this in a html file.
Use the [hidden] attribute for the component, to specify a flag variable responsible for hiding it.
When this variable is set to true or 1, the component is hidden otherwise it is shown.
Here is an example of how the [hidden] attribute is used:

<app-infobox [hidden]=”hidefooter class=“infobox col-md-4” *ngIf=“Display(‘all’)”></app-infobox>

Note that [hidden] in a way simply sets the css of the component as { display: none }, whereas in *ngIf, the component is not loaded in the DOM.
So, in this case unless Display(‘all’) returns true the component is not even loaded to the DOM but if [hidden] is set to true, then the component is still present, only not displayed.
In the typescript files, here is how the two tasks are performed:
To hide components of the page, until all the search results load.

this.querychange$ = store.select(fromRoot.getquery);
this.querychange$.subscribe(res => {
this.hidefooter = 1;

this.responseTime$ = store.select(fromRoot.getResponseTime);
this.responseTime$.subscribe(responsetime => {
this.hidefooter = 0;

The component is hidden when the query request is just sent. It is then kept hidden until the results for the previously sent query are available.

2. To hide components of the page, if they were meant to appear only in particular cases.
For example, if you wish to show a component like Autocorrect only when you are on the first page of the search results, here is how you can do it:

if (this.presentPage === 1) {
this.hideAutoCorrect = 0;
} else {
this.hideAutoCorrect = 1;
}

This should hopefully give you a good idea on how to use the hidden attribute. These resources can be referred to for more information.

Implementing Themes in Angular JS for Susper

Adding themes to any website, makes it more interesting to use, and also helps customize the website according to personal preferences. This blog deals with how we implemented themes in Susper.
Susper offers the following themes

  • Default
  • Dark
  • Basic
  • Contrast
  • Terminal

This is how some of the themes look
Dark:

Contrast:  
Terminal:
Lets go through a step by step guide how to implement this:

  1. Add a Themes service  (In app/src/theme.service.ts in Susper)

Here is the code snippet:

import { Injectable } from ‘@angular/core’;

@Injectable()
export class ThemeService {

public titleColor: string;
public linkColor: string;
public descriptionColor: string;
public backgroundColor: string;

constructor() { }

}

  1.  Create a component for themes, and define functions for various themes in the .ts file.(src/app/theme/theme.component.ts in Susper)

Here is the example code:

import { Component, OnInit } from ‘@angular/core’;
import { ThemeService } from ‘../theme.service’;@Component({
selector: ‘app-theme’,
templateUrl: ‘./theme.component.html’,
styleUrls: [‘./theme.component.css’]
})
export class ThemeComponent implements OnInit {constructor(
private themeService: ThemeService
) { }ngOnInit() {
}darkTheme() {
this.themeService.backgroundColor = ‘#FFFFFF’;
this.themeService.titleColor = ‘#050404’;
this.themeService.linkColor = ‘#7E716E’;
this.themeService.descriptionColor = ‘#494443’;
}

defaultTheme() {
this.themeService.backgroundColor = ‘#FFFFFF’;
this.themeService.titleColor = ‘#1a0dab’;
this.themeService.linkColor = ‘#006621’;
this.themeService.descriptionColor = ‘#545454’;
}

basicTheme() {
this.themeService.backgroundColor = ‘#FFFFFF’;
this.themeService.titleColor = ‘#1a0dab’;
this.themeService.linkColor = ‘#494443’;
this.themeService.descriptionColor = ‘#7E716E’;
}

In the above code, the first few lines  include the constructor, which defines the theme service, and include a default function that runs as soon as the page is initialized.

We then see three kinds of themes implemented, dark, default and contrast. Let us examine the darkTheme:

darkTheme() {
this.themeService.backgroundColor = ‘#FFFFFF’;
this.themeService.titleColor = ‘#050404’;
this.themeService.linkColor = ‘#7E716E’;
this.themeService.descriptionColor = ‘#494443’;
}
  • The first line sets the background color of the screen(to white).
  • The second line is used to set the color of all the titles of the search results
  • The third line is used to set the link/url color
  • The fourth line sets the description color
    1.   Link the appropriate attributes in your html pages, using [style.’css-attribute’]

(src/app/results/results.component.html in Susper)

Following this example, you can link different parts of the html file to the attributes in your theme service and you are done!
If implementing this in a project like Susper, a few points of caution:

  • Make sure you write your spec.ts file well, and add your component for proper compilation and testing.
  • Do not forget to import the service into any component before you use it in its html files.

Resources 

Creating nested routes in Open Event Front-end and Navigating them with Tabs via semantic UI – Ember Integration

Semantic UI is a modern development framework which helps build responsive and aesthetically beautiful layouts. While it is a really powerful framework in itself, it additionally offers seamless integrations with some of the other open source frameworks including ember js.

Open Event Front-end is a project of FOSSASIA organisation, which was created with the aim of decoupling the front end and the back end for the open event orga server. It is primarily based on ember JS and uses semantic UI for it’s UI.

Here we will be making a nested route /events/ with /events/live/, events/draft, events/past , events/import as it’s subroutes.

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

$ ember generate route events

Then we go on to generate the successive sub routes as follows

$ ember generate route events/live
$ ember generate route events/past
$ ember generate route events/draft
$ ember generate route events/import

The router.js file should be looking like this now.

this.route('events', function() {
    this.route('live');
    this.route('draft');
    this.route('past');
    this.route('import');
  });

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

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

Next, we go to the template file of events/ route which is at templates/events.hbs And write the following code to create a menu and use ember integration of semantic UI link-to to link the tabs of the menu with the corresponding correct route. It will take care of selecting the appropriate data for the corresponding route and display it in the correct tab via the outlet

<.div class="row">
  <.div class="sixteen wide column">
    <.div class="ui fluid pointing secondary menu">
      {{#link-to 'events.live' class='item'}}
        {{t 'Live'}}
      {{/link-to}}
      {{#link-to 'events.draft' class='item'}}
        {{t 'Draft'}}
      {{/link-to}}
      {{#link-to 'events.past' class='item'}}
        {{t 'Past'}}
      {{/link-to}}
      {{#link-to 'events.import' class='item'}}
        {{t 'Import'}}
      {{/link-to}}
    <./div>
  <./div>
<./div>
<.div class="ui segment">
  {{outlet}}
<./div>

So finally, we start filling in the data for each of these routes. Let’s fill some dummy data at templates/events/live.hbs

<.div class="row">
  <.div class="sixteen wide column">
    <.table class="ui tablet stackable very basic table">
      <.thead>
        <.tr>
          <.th>{{t 'Name'}}<./th>
          <.th>{{t 'Date'}}<./th>
          <.th>{{t 'Roles'}}<./th>
          <.th>{{t 'Sessions'}}<./th>
          <.th>{{t 'Speakers'}}<./th>
          <.th>{{t 'Tickets'}}<./th>
          <.th>{{t 'Public URL'}}<./th>
          <.th><./th>
        <./tr>
      <./thead>
      <.tbody>
        <.tr>
          <.td>
            <.div class="ui header weight-400">
              <.img src="http://placehold.it/200x200" alt="Event logo" class="ui image">
              Sample Event
            <./div>
          <./td>
          <.td>
            March 18, 2016 - 09:30 AM
            <.br>(to)<.br>
            March 20, 2016 - 05:30 PM
          <./td>
          <.td>
            <.div class="ui ordered list">
              <.div class="item">[email protected] ({{t 'Organizer'}})<./div>
              <.div class="item">[email protected] ({{t 'Manager'}})<./div>
            <./div>
          <./td>
          <.td>
            <.div class="ui list">
              <.div class="item">{{t 'Drafts'}}: 0<./div>
              <.div class="item">{{t 'Submitted'}}: 0<./div>
              <.div class="item">{{t 'Accepted'}}: 0<./div>
              <.div class="item">{{t 'Confirmed'}}: 0<./div>
              <.div class="item">{{t 'Pending'}}: 0<./div>
              <.div class="item">{{t 'Rejected'}}: 0<./div>
            <./div>
          <./td>
          <.td>
            2
          <./td>
          <.td>
            <.div class="ui bulleted list">
              <.div class="item">{{t 'Premium'}} (12/100)<./div>
              <.div class="item">{{t 'VIP'}} (10/15)<./div>
              <.div class="item">{{t 'Normal'}} (100/200)<./div>
              <.div class="item">{{t 'Free'}} (100/500)<./div>
            <./div>
          <./td>
          <.td>
            <.div class="ui link list">
              <.a class="item" target="_blank" rel="noopener" href="http://nextgen.eventyay.com/e/ecc2001a">
                http://nextgen.eventyay.com/e/ecc2001a
              <./a>
            <./div>
          <./td>
          <.td class="center aligned">
            <.div class="ui vertical compact basic buttons">
              {{#ui-popup content=(t 'Edit event details') class='ui icon button'}}
                <.i class="edit icon"><./i>
              {{/ui-popup}}
              {{#ui-popup content=(t 'View event details') class='ui icon button'}}
                <.i class="unhide icon"><./i>
              {{/ui-popup}}
              {{#ui-popup content=(t 'Delete event') class='ui icon button'}}
                <.i class="trash outline icon"><./i>
              {{/ui-popup}}
            <./div>
          <./td>
        <./tr>
      <./tbody>
    <./table>
  <./div>
<./div>

 Similarly we can fill the required data for each of the routes.And this is it, our nested route is ready. Here is a screenshot what you might expect.

Screenshot highlighting the tabs

Resources