9/29/15

is it possible to track your visitors individual behavior?

The short answer is "Yes, sure", but there are lot of options how to do it. I will talk now about how to track behavior using google analytics.
I guess  everyone knows how to implement google analytics at your website  - to create account at google analytics and to paste the code snippet like:


    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'your-account-num']);
    _gaq.push(['_trackPageview']);

    (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    })();


This great tool of google makes you able to analyze your website traffic (like getting reports) 

 








without writing any code!
 
The problem with tracking individual user's behavior is that you need analytics service to know the id/name of specific user. Thats where angularticks library can be usefull.
This lybrary enables you easily fire events with data you choose to send:

 var eventData = {  
  category: 'motorcycle-details',
  label: user+' viewed '+$routeParams.productname +' motorcycle'
 };
 $analytics.eventTrack('viewing-motorcycle-details', eventData);

If user name passed through the event parameres the reports will show results with his behavior:



 

9/19/15

how to make google to index your angular single page app

The problem with angular single page app (which uses router module) is that google crawler cannot access route view dynamic content.
When processing such pages (like mysite.com/#/product/zzr1400) google crawler sees only template with "{{something should be here}}" tags.

I have created this site for experimenting purposes.
According to little research I did (see this grate tutorial and here) for make your site to be indexed by google you need to serve crawler bot static HTML snapshots made from your dynamic views.

Steps

For doing it you need to perform following steps:
1. place following tag in the head of your index page:

<meta name="fragment" content="!">

2. make sure your router configured to use "!" or html5 mode

  $locationProvider.html5Mode(true);
  $locationProvider.hashPrefix('!');

This tells crawler to access the site using "?_escaped_fragment_=" quesry string.
3. configure your server or use some online services like Prerender.io or brombone to serve static html snapshots of your content.

How to check you pages are cached for crawler?

If you using prerender.io online service:
Access your dashboard:

You should see the table of your pages in the "cached pages" section.

6/15/15

Interesting thing about $watch

I guess everybody knows how to make angular to respond on the changes made to scope variables- to add some callback to "$watch" , something like this:

scope.$watch('name', function(newValue, oldValue) {
  scope.counter = scope.counter + 1;
});

But the cool thing that $watch can listen and respond to changes of functions which return something too. For example:

 $scope.tabs=[{active:true, name:'first'},{active:false, name:'second'},{active:false, name:'third'}}];
 $scope.$watch(function(){ return $scope.tabs.filter(function(t){ return t.active; })[0]; },function(tab){
   $scope.$broadcast('tab-selected',{name:tab.name})
 },true)

6/8/15

Building & testing user-menu directive part 2

testing

first test

after we have build user menu directive in previous post we may want to test it...
It is a good practice to test things you did. (Actually better practice is to test things before you did them, but lets leave TDD and focus on only T...) So lets start doing some testing:
1. lets test firstly that our directive loading a template correctly:


describe('Testing user menu directive', function() {
  var $scope = null;
  var $compile;

  //you need to indicate your module in a test
  beforeEach(module('plunker'));
  beforeEach(inject(function($templateCache, _$compile_) {
    $compile = _$compile_;
    var directiveTemplate = null;
    var req = new XMLHttpRequest();
    req.onload = function() {
        directiveTemplate = this.responseText;
    };
 
    // Using `false` as the third parameter to open() makes the operation synchronous.
    req.open("get", "userMenuTpl.html", false);
    req.send();
    $templateCache.put("userMenuTpl.html", directiveTemplate);
  }));
  
  beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new();

  }));

  it('should display login menu', function() {
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest()              
    expect($(element).find('a:eq(0)').text()).toContain('Sign in');
  });  
});

So far so good...

Slight Modifications

Now to more complicate things - lets add "signout" functionality which will cause our directive to change its view dynamically:


var app = angular.module('plunker', [])
.directive('userMenu', function ($templateCache) {
    return {
        restrict: 'E', 
        templateUrl:'userMenuTpl.html',
        controller: function ($scope, userSvc) {
            if (userSvc.isAuthenticated()) {
                $scope.profile = userSvc.getProfile(); 
            }
            $scope.getprofile = function(){
              return  userSvc.getProfile()
            }
            $scope.signout = function(){
              userSvc.logout();
            }
        }
    }
})
.factory('userSvc', function () {
      
    var _isAuthenticated = false, _profile= null;
    return {
      isAuthenticated: function() {
        return _isAuthenticated;
      },
      getProfile: function() {
        return _profile
      },
      login: function(){
        _isAuthenticated = true;
        _profile = {username:'Shlomo'}
      },
      logout:function(){
        _profile = _isAuthenticated = false;
      }
    }
})

lets modify the template also


<ul class="nav navbar-nav navbar-right" ng-if="getprofile()">
  <li>
    <a href="/profile"><i class="glyphicon glyphicon-user">
      </i> {{profile.username}}
    </a>
  </li>  
  <li style="border-left:1px solid #fff">
     <a href ng-click="signout()">Sign out</a>  
  </li>
</ul>

<ul class="nav navbar-nav navbar-right" ng-if="!getprofile()">
  <li>
    <a href  ng-click="open()">Sign in</a>  
  </li>  
  <li>
    <a style="border-left:1px solid #fff" href="/link-to-login">Sign Up</a>  
  </li>
</ul>

more tests

So now our directive able to change it view after user clicking on some link. lets test those behaviors:


  //...
  it('when logged in should show "sign out" link and user name', function() {
    userSvc.login();
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest()              
    expect($(element).find('a:eq(1)').text()).toContain('Sign out');
    expect($(element).find('a:eq(0)').text()).toContain('Shlomo');
  });
  
  it('after loggin out- should show "Sign in" link again', function() {
    userSvc.login();
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest();              
    expect($(element).find('a:eq(0)').text()).toContain('Shlomo');
    expect($(element).find('a:eq(1)').text()).toBe('Sign out');
    element.find('a').triggerHandler('click');
    expect($(element).find('a:eq(0)').text()).toBe('Sign in');
  }); 
 //...

View it on plunker

Hope you have good time reading

5/13/15

Building & testing user-menu directive part 1

Part1


scenario

Consider the following scenario:
At the right of top navbar of the application screen, user should see his username   and "sign out" link while he is loggedin:

loggedin view


and when he is  not logged in - "signin" and "singnup" links should be displayed.

not-loggedin view



directive

Lets build simple directive user-menu  which will be placed into top navbar of our page:


<ul>
    <user-menu></user-menu>
</ul>


usermenu.js

angular.module("myApp")
.directive('userMenu', function () {
    return {
        restrict: 'E', 
        templateUrl: 'usermenuTpl.html',
        controller: function ($scope, userSvc) {
            if (userSvc.isAuthenticated()) {
                $scope.profile = userSvc.getProfile(); 

           }
        }
    }
})



userMenuTpl.html

<ul class="nav navbar-nav navbar-right" ng-if="profile">
  <li>
    <a href="/profile"><i class="glyphicon glyphicon-user">
      </i> {{profile.username}}
    </a>
  </li>  
  <li style="border-left:1px solid #fff">

     <a href="/link-to-logout">Sign out</a>  
  </li>
</ul>

<ul class="nav navbar-nav navbar-right" ng-if="!profile">
  <li>
    <a href  ng-click="open()">Sign in</a>  
  </li>  
  <li>
    <a style="border-left:1px solid #fff" href="/link-to-login">Sign Up</a>  
  </li>
</ul>



All things are simple - directive consists from 2 templates, and display each wether "profile" property is set or not.
"Profile" property will came from some User service (which usually should access to DB, but for being simple lets fake it)


userSvc.js

angular.module("myApp")
        .factory('userSvc', function () {
            return {
             isAuthenticated: function () {
                return true;                
             },
             getProfile: function () {
                return {username: 'Shlomo'};
             }
           }
        })



for testing manually lets see what happens when we changing code of isAuthenticated method so it returns false. Yes - we see signin & signup links.
So far so good
Hope you have fun reading

Part 2

5/5/15

Interesting thing about ui router "resolve"

What is resolve

Many angluar applications use  ui.router. This plugin makes the job of changing views easier and code responsible for view managing - more accurate. Also it makes the view accessible from the  url (deep linking) - which means application users can bookmark the certain view or send it by email.

This post is about one interesting feature of the ui.router (and also ng-router) - the resolve.
You can pass some data to the view when it initiated, by putting the service call into the "resolve" block:


var List  = ['itemsSvc', function(itemsSvc){
  return itemsSvc.get();
}],

$stateProvider
.state('app.items', {
  url: '/items',  views: {
    'tabpanel': {
      templateUrl: 'views/items.html',      
      controller: 'itemsCtrl'    
     }
  },
  resolve:{
    items: List
  }
})



after loading the resolve item can be accessed inside the controller - as any other of injectables:


angular.module('My')
  .controller('ItemsCtrl', function ($scope, items) {
    $scope.list =  items;
    ...


Using resolve ensures that view will not be rendered until the request specified inside the resolve will return back.

You can update the collection cached inside the resolve.
Consider you have following UX scenario:
The "add item" form is located on different  child view:


var List  = ['itemsSvc', function(itemsSvc){
  return itemsSvc.get();
}],

$stateProvider
.state('app.items', {
  url: '/items',  views: {
    'tabpanel': {
      templateUrl: 'views/items.html', 
      controller: 'itemsCtrl'    
    }
  }, 
  resolve:{
    items: List
  }
})
.state('app.items.new', {
  url: '/new',  views: {
    'details@app': {
      templateUrl: 'views/itemForm.html', 
      controller: 'itemCtrl'    
    }
  }
})


After successfully adding new item user should be redirected to the parent list view. But how to make the list view to show the newly added item?

You can do it by updating items collection you get from resolve:


angular.module('My')
  .controller('AddEditItemCtrl', function ($scope, items) {
   $scope.list =  items;
   $scope.add=function(e){

      $scope.newItem = { Id: '111', name:'some item'};
      $scope.list.push(angular.copy($scope.newItem));
      $state.go('app.items')
   };
};


After redirecting to "items" view you will see the new items is already here!

4/30/15

Cool Trick For Unit Testing The Angular Controller

Almost always when writing unit tests for controller we need somehow to handle the fact that controller calls to some service/factory.

Lets look at following controller:



angular.module('siteApp')
        .controller('RegisterCtrl', function (
        $scope,
         auth,
         $location,
         $interval) {

            $scope.user = {};
            $scope.messages = [];


            var success = function(){
                $scope.messages.push({
                    type:'success',
                    text:'Congratulations,You are registered user!'
                });             
                $interval(function(){$location.path('/login')},6000,1)
            }, error =function(){
                $scope.messages.push({
                   type:'danger',
                   text:'error occurred during registration'
                });                
            };

            $scope.save = function(){
                auth.register($scope.user).then(success, error);         
            }

  });



The controller uses "auth" service and its "register" method, which sending a request.
We can mock the request with $httpBackend service which test if request is sended.


it('after clicking "save" some request should be sent', function () {
  initCtrl()
  $httpBackend.expectPOST('/api/register').respond(200, {});
  $scope.save();
  $httpBackend.flush();
});


Since we trying now to test the logic of the controller (not the service)- I think better practice is to mock the  service method and only test if it been called with proper parameters:



it('after clicking "save" auth.register should call', function () {

    initCtrl()
    spyOn(auth, 'register').andReturn($q.when()); 
    $scope.save();
    expect(auth.register).toHaveBeenCalledWith($scope.user);

    $scope.$digest();   //important!!!
    expect($scope.messages.length).toBe(1)


});


Notice that for testing the messages array you need 

  $scope.$digest(); 

statement for update the scope.

Hope you have fun reading...


4/29/15

Taking Advantage Of Bootstrap Mixins

Recently i took care of some css bug. Some transformation effect didnt looked good at IE.




 After examination of  less file i saw following code:

.parnetDetailsButton {
  float: left;  
  margin-top: 50px;  
  margin-left: -14px;  
  font-size: 15px;
  -webkit-transform-origin-x: 23px; 
  -webkit-transform-origin-y: 13px;
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}



Quick look on this code shows that the reason the effect not acting correctly in IE is - missing microsoft prefix on transform and transform-origin  rules. It should be prefixed with "ms"

-ms-transform-origin: 23px 13px;

That can be quickly fixed by adding proper prefix.
But since we already using bootstrap i decided to take advantage of some of bootstrap less mixins:
.rotate and .transform-origin
mixins are like functions in the less language - they can get parameters and give output.
for using mixins we need the utils of bootstrap to be imported in our less file.

@import "../bower_components/bootstrap/less/mixins/vendor-prefixes";


now this code can be written in more accurate way:


.parnetDetailsButton {
  float: left;
  margin-top: 50px;
  margin-left: -14px;
  font-size: 15px;  
  .rotate(90deg);
  .transform-origin(23px 13px);
}



after less compiled it converted to following css:

.parnetDetailsButton {
  float: left;   
  margin-top: 50px; 
 margin-left: -14px;    
  font-size: 15px;  
 -webkit-transform: rotate(90deg);    
  -ms-transform: rotate(90deg);      
   -o-transform: rotate(90deg);        
     transform: rotate(90deg);  
-webkit-transform-origin: 23px 13px;   
  -moz-transform-origin: 23px 13px;    
   -ms-transform-origin: 23px 13px;     
    transform-origin: 23px 13px;
}


Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...