Wednesday, December 9, 2015

AngularJS: Getting started

Introduction

AngularJS is one of the most popular frameworks for web UI development. Following are some major features of the framework:
  • Makes DOM manipulation easy.
  • Segregates back-end (JavaScript) from UI (html).
  • Provides dependency injection.
  • Easy to develop Single Page Application (SPA) using the routing support.
Instead of directly jumping into the AngularJS features like modules, controller, services etc. let's try to build a very basic UI using AngularJS and understand how AngularJS works.

Sample UI - Traditional way

Let's try to develop following UI using traditional approach and then using AngularJS.

This is very basic UI displaying the administrator details like name and email address. Then there is a checkbox to toggle display of some advanced options. If checked it will show advanced options as follows:

HTML markups

The HTML snippet for the UI will be as follows:

 <div>
   <h2>Administrator</h2>
   <label><b>Name: </b></label><span id="lblAdminName"></span>
   <label><b>Email: </b></label><span id="lblAdminEmail"></span>
   <input id="chkAdvanceOptions" 
          type="checkbox" 
          onclick="toggleShowAdvanceOptions();">
      <label>Show Advance Options</label>
   <div id="divAdvanceOptionsstyle="display: none">
      <b>Advance options here...</b>
   </div>
 </div>

Populating administrator details

The UI can be populated using following JavaScript:

function initializeSettings () {

   var administrator = getAdministratorDetails();           
   document.getElementById('lblAdminName').innerHTML =   administrator.name;  
   document.getElementById('lblAdminEmail').innerHTML = administrator.email; 
} 

function toggleShowAdvanceOptions() { 
   
  var displayAdvanceOptions = document.getElementById('chkAdvanceOptions').checked;        document.getElementById('divAdvanceOptions').style.display 
                            = (displayAdvanceOptions ? 'block' : 'none'); 
} 

function getAdministratorDetails() { 
   // This will come from a REST call. 
   return { name: 'Administrator', email: 'administrator@company.com' }; 
}


Now, above UI will be populated using JavaScript as follows:
  • Fetch the administrator details from server, say, using REST calls.
  • To populate the administrator name get hold of the <span> element using the id i.e. lblAdminName e.g. document.getElementById("lblAdminName") or using jQuery.
  • Set the value on the DOM element. Same will be the case for displaying the email address.

Checkbox handler

While fetching and populating the administrator details seems trivial, toggling the advance options required some more work:
  • Associate a JavaScript function handler for checkbox click. In this example it is toggleShowAdvanceOptions();
  • In this handler find out if the checkbox is checked or not. Depending on this, toggle the visibility of the div displaying advance options.


Issues with traditional approach

One may feel that with above approach the UI and backend are segregated i.e. UI is in *.html or *.jsp file with HTML markups and backend is in *.js i.e. JavaScript file. But segregation does not mean just separating these out. Let's try to answer a simple question: Who is responsible for how the UI looks and behave? Obviously, *.html defines the UI in terms of look and feel while *.js provides the required data. But wait, the *.js file not just provides the data, it actually pushes the data to UI. Ok, then who is responsible for toggling the display of advance options? Hmmm, *.html hooks the checked event to backend and then backend toggles the visibility. 
To summarize, the responsibility to render the UI is with both *.html and *.js files. The reality is that we cannot avoid such a situation i.e. dynamic content needs to be handled by JavaScript. So how does AngularJS help here? In simple terms, it allows us to define the UI and it's behavior in *.html files while the JavaScript files needs to be responsible for fetching the data or reacting to events. But in no way the JavaScript files will deal with UI elements i.e. no more document.getElementById() or jQuery calls in backend. Let's see how this is possible.

Sample UI - AngularJS way

With AngularJS we are not doing away with *.html or *.js files. Instead of getting directly into AngularJS concepts, let's first see how these files are defined for AngularJS based web application.

JavaScript object

Let's start with the JavaScript object that will be responsible for populating the data on UI.

 function SettingsController () {
  // Get this from REST service
  this.administrator = {
    name: 'Administrator',
    email: 'administrator@company.com'
 };

 this.showAdvanceOptions = false;
}

As seen above the JavaScript object simply has two properties:
  • administrator: This property will hold the json describing administrator name and email. It should be populated using REST call. For this example, we have hard-coded it.
  • showAdvanceOptions: This property tells if the advance options should be displayed or not. The default value is false.
That's it. No getElementById() or any DOM manipulation code here. 
Now, let's look at the HTML.

HTML markups

The HTML snippet for the UI will be as follows:

1: <div ng-controller="myapp.settings.controller as ctrl">
2:  <h2>Administrator</h2>
3:  <label><b>Name: </b></label><span>{{ctrl.administrator.name}}</span>
4:  <label><b>Email: </b></label><span>{{ctrl.administrator.email}}</span>
5:    <input type="checkbox" ng-model="ctrl.showAdvanceOptions">
6:       <label>Show Advance Options ({{ctrl.showAdvanceOptions}})</label>
7:    <div ng-show="ctrl.showAdvanceOptions">
8:       <b>Advance options here...</b>
9:    </div>
10:</div>

The markup is same as for traditional approach except that there are additional attributes. But in this case we are just defining how the UI is rendered and behaves in markup itself. Let's look at the markup one by one.
  • Line 1: 
    • On the <div> tag we are specifying which JavaScript object will be responsible for providing the data to this UI.
    • ng-controller is AngularJS specific attribute which defines the JavaScript object to be used.
    • For this discussion, assume that we have a map of name to JavaScript object. Hence, we are asking AngularJS to lookup that map with the name as 'myapp.settings.controller' and attach the JavaScript object to this UI. For now, just ignore how we are registering the object with AngularJS.
    • Also, we are using an alias 'ctrl' for that object.
  • Line 3: 
    • Here, we have used {{ctrl.administrator.name}}
    • This tells AngularJS that on the JavaScript object attached to this UI, we need to look for a property named administrator. Since, administrator value is a json we are further referring to the name property in it. 
    • The value for the administrator.name property will get populated on UI.
    • If the value changes in backend, then it will also get updated on UI.
    • Same is the case for {{ctrl.administrator.email}}
  • Line 5:
    • Here, on the checkbox, we have used the ng-model attribute.
    • The value for this attribute is the property on our JavaScript object.
    • It tells AngualrJS that value for this checkbox should be deduced from the showAdvanceOptions property on JavaScript object.
    • In addition to that it also tells that when the checkbox value changes from UI, say, on user click, then the value on JavaScript object should also be updated. This is called two- way binding in AngularJS.
  • Line 6:
    • Here we are displaying the value of showAdvanceOption property on the JavaScript object.
    • When the checkbox is checked then the value becomes true else false.
  • Line 7:
    • Here we are controlling the visibility of the div using the ng-show attribute of AngularJS.
    • It tells AngularJS that the div should be visible when the showAdvanceOptions property on the JavaScript object is true else it should be hidden.
    • Now, the showAdvanceOptions property is true/false depending on the state of the checkbox and hence the div will be visible if checkbox is checked else it will be hidden.

Summary

As seen above, using AngularJS the HTML markup defined what data needs to be rendered and how. The JavaScript object, called as controller, only needs to provide the required data to UI. A lot happens internally using AngularJS.
In order to implement event a simple UI using AngularJS one needs to understand controllers, services, modules etc. But this post should help understand the very basic of how controller can be used to provide data to UI and how the HTML markups drive the UI flow.
In next post we will have a simple MVC application using AngularJS and explore the controllers, services, modules etc.

Tuesday, December 1, 2015

Elasticsearch: What you see is NOT what you get !!!

Recently, I troubleshooted two common issues with ES queries. Suddenly, the queries stopped returning data using some filter criteria. But when we looked at all the records for the type then the filter should have evaluated to true and returned data. We often forget that ES mostly returns original document which was indexed. But while indexing the fields in document there are analyzers being applied. So the field value we see in document is not exactly same as indexed value. 

Consider the following simple example:
















Line 1: We have created an employee record with displayName as "Ajey Dudhe".
Line 6: We are retrieving all the employee records.

If you see on the right hand side under _source the document is returned as it is. But does Elasticsearch see the value of displayName as "Ajey Dudhe". The answer is no. In this example, when the document is inserted then default analyzers are used. By default the string value will be broken into tokens and lower-cased. And this is the value which Elasticsearch sees. In order to have a look at the actual value visible to Elasticsearch we need to use the fielddata_fields option while fetching the documents as follows:


















Line 22 on right hand side: Notice that the fielddata_fields now has values as seen by ES i.e. tokenized and lower-cased. In case the value is not at all indexed by ES then it will not appear under fields.