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!

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...