Showing posts with label angularjs2. Show all posts
Showing posts with label angularjs2. Show all posts

4/14/16

Events In Angular2

Like in angular1 you can use events in angular2. What events good for? To make distant components to notify one to another.

How to send event

Everybody knows that for sending event you need to create some kind of channel. This channel will be used to publish(emit) event and to subscribe to event for getting notified . Angular2 has its own EventEmitter class just for this scenario


import {Injectable, EventEmitter} from 'angular2/core';
@Injectable()
export class EmitterService {
  private static _emitters: { [channel: string]: EventEmitter } = {};
  static get(channel: string): EventEmitter {
    if (!this._emitters[channel]) 
      this._emitters[channel] = new EventEmitter();
    return this._emitters[channel];
  }
}

This service sets some channel (if no channel exists) and stores it in the private member

Challenge

In this post i want to demonstrate usage of events by bulding a refresh chart button, of the panel on which a flot component from older posts is located.
pressing on this button should refresh the data of the chart in the child flot component.

Click Event

Sending and catching click event is pretty straightforward:
Sending


<a href="#" panel-refresh="" (click)="chartRefresh();false">
    <em class="fa fa-refresh"></em>
</a>

Catching

  ...
  chartRefresh(){
   this.getEntries();
  }
  ...

The problem is to send custom event from parent component panel
to its child conmponent flot that will cause chart to redraw itself.
This event must be fired after data loaded through ajax request.


  ...
    this.f.getFlotEntries()
      .subscribe(entries => {
        //here event is emitted
        this.e.emit('Broadcast');
      },
  ...

Full Code Of Panel Component


import {Component} from 'angular2/core'
import {FlotCmp} from './flot';
import {FlotService} from './FlotService';
import {EmitterService} from './EmitterService';
import {Observable}     from 'rxjs/Observable';
import {HTTP_PROVIDERS}    from 'angular2/http';
import {Http, Response} from 'angular2/http';
@Component({
  selector: 'panel',
  providers: [],
  template: `
 <div class="panel panel-default">
    <h2 class="panel-heading">
    <div class="pull-right">
    <a href="#" panel-refresh="" (click)="chartRefresh();false"><em class="fa fa-refresh"></em></a>
    </div>
    <div>panel heading</div>
    </h2>
    <flot class="panel-body" height="250px" width="100%"></flot>
 </div>
  `,
  directives: [FlotCmp], 
  providers: [
    FlotService,
    EmitterService,
    HTTP_PROVIDERS
  ] 
})
export class Panel {
  
  constructor(private f:FlotService,private e:EmitterService) {
    
    this.e = EmitterService.get("channel_1"); 
    this.getEntries();
  }
  getEntries(){
    this.f.getFlotEntries()
      .subscribe(entries => {
        //here event is emitted
        this.e.emit('Broadcast');
      },
      error =>  {
        this.errorMessage = error;
         console.log(error);
      });    
    
  }
  chartRefresh(){
   this.getEntries();
  }
}

Handling event

Child component flot responding to custom event from its parent.


   ...
   this.emitter.subscribe(msg => {      
       $.plot( plotArea, this.dataset, this.options);
   });
   ...

3/27/16

Using Promises In Angular2

In the last post we created our first angular service. This service served only some static data for chart componentt we build in previous posts.
Everybody knows that to serve static data is not cool.
The really though guys use services for bring data from server.
In other words - real service must use is promises.

Promises In Angular2

For one who used promises in angular 1. to use promise in angular2 is pretty simple:


@Injectable()
export class FlotService {
  getFlotEntries() {
    return Promise.resolve(FlotEntries);
  }
}

Yes, the same old good Promise.resolve. And for consume the service we should use the same old "then"

export class App implements OnInit {
  
  private dataset:any;
  public entries;  
  getEntries() {
    this._flotService.getFlotEntries().then(
                       entries => this.dataset[0].data = entries,
                       error =>  this.errorMessage = error);
  } 

Now lets make our chart component to work with this service.

Projecting data into component

The issue i had run into is that while the call to the service is on the main "App" component, the place i want data to be displayed is on the inner component - "Flot", which getting chart "data" property as input parameter:


<flot  [options]="splineOptions" [dataset]="dataset" height="250px" width="100%"></flot>

In angular 1. all data changes are observed and immediately projected after the change is located by digest cycle.
In angular2 - not in all cases. Angular2 change detection doesn't observe the content only the value or reference itself (angular2 observes only "dataset" but not inner property "data")

A workaround

One way to make angular2 to watch the inner properties changes is to use ngDoCheck().
ngDoCheck() is called every change detection cycle, whether or not there are any input property changes.(great thanks to these guys for this information)
Now we need to modify flot component - so it start to use OnChanges:


import {Component, ElementRef, Input} from 'angular2/core';

@Component({
  selector: 'flot',
  template: `
loading
` }) export class FlotCmp{ ... ngOnInit() { if(!FlotCmp.chosenInitialized) { let plotArea = $(this.el.nativeElement).find('div').empty(); plotArea.css({ width: this.width, height: this.height }); $.plot( plotArea, this.dataset, this.options); FlotCmp.chosenInitialized = true; } } ngDoCheck() { if(this.dataset[0].data !== null && !this.dataPlotted) { console.log('plotting data'); let plotArea = $(this.el.nativeElement).find('div').empty(); $.plot( plotArea, this.dataset, this.options); this.dataPlotted = true; } } }
Now the child component view reacts to changes of flot data

Here is running code: Hope you have fun reading...

2/23/16

Using Angular2 Services

Remember we created flot component in previous posts?
Lets have a quick look on the code now:


export class App {  
  constructor() {
    ...
    //Look here - dont you feel something here is uuuugggglllyyyy?
    this.dataset = [{label: "line1",color:"blue",data:[
    [1, 130],
    [2, 40],
    [3, 80],
    [4, 160],
    [5, 159],
    [6, 370],
    [7, 330],
    [8, 350],
    [9, 370],
    [10, 400],
    [11, 330],
    [12, 350]
]}];
  }
}

The way dataset property sat - is perfect example of hardcoding, and everybody knows that hardcoding is bad.
Since we dont have real server to get data from yet, at least lets move the data-getting logic to some different module, in other words -

Lets Create A Service


export var FlotEntries: Array[] = [
    [1, 130],
    [2, 40],
    [3, 80],
    [4, 160],
    [5, 159],
    [6, 370],
    [7, 330],
    [8, 350],
    [9, 370],
    [10, 400],
    [11, 330],
    [12, 350]
];

//@Injectable()
export class FlotService {
  getFlotEntries() {
    return FlotEntries;
  }
}

This service doing only one thing - getting data for a plot chart.

Lets Use Our Service

Last thing that left - is to teach our main component how to use the service:
Note: Dont forget to list the service inside "poviders" property


//our root app component
import {Component} from 'angular2/core'
import {FlotCmp} from './flot';
import {FlotService} from './FlotService';

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <flot  [options]="splineOptions" [dataset]="dataset" height="250px" width="100%"></flot>
    </div>
  `,
  directives: [FlotCmp],
  providers: [FlotService]//important!!!
})
export class App {
  
  constructor(private _flotService:FlotService) {
    this.name = 'Angular2'
    this.splineOptions: any = {
            series: {
                lines: { show: true },
                points: {
                    radius: 3,
                    show: true
                }
            }
    };
    this.dataset = [{label: "line1",color:"blue",data:this._flotService.getFlotEntries()]}];
  }
}


Looks better isn't it?
plunkr
Thats it, now you know how to create angular2 service.
Hope you have fun reading

2/6/16

bootstrap collapsible directive in angular2

I decided to to take another looking easy challenge - to make collapsible directive with angular2 by myself.
I will use css of great bootstrap framework.
Bootstrap framework has collapsed CSS class for make elements disappear (by applying "display:none" style), and "in" class, which when it set on collapsed element - makes it to show up.
So, once we already know (after doing couple of things in previous posts) how to create some angular2 components -

Lets create parent component (App)


//our root app component
import {Component} from 'angular2/core'
import {Collapse} from 'src/collapse'
@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2 (click)="isCollapsed = !isCollapsed">click here</h2>
       
      <div class="collapse in" [collapse]="isCollapsed" >
        <div class="well">
          panel panel panel
        </div>
      </div>
      
    </div>
  `,
  directives: [Collapse]
})
export class App {
  isCollapsed:boolean = false;
  constructor() {}
} 

Directive is not dead

Yes there are directives in angular2 too. Their goal is to be decorator-style directives, which do not have templates. According to angular2 documentation "directive changes the appearance or behavior of a DOM element."
Note that for changing class property of directive's root element i go to use HostBinding factory

import {Directive, Input, HostBinding} from 'angular2/core'
@Directive({selector: '[collapse]'})
export class Collapse { 
 

  @HostBinding('class.in') 
  private isCollapsed:boolean;
  
  
  @Input()
  private set collapse(value:boolean) { 
     
      if(!value){   
        this.isCollapsed = true; 
      }else { 
        this.isCollapsed = false; 
      }
     
  }
  constructor() {}  
}


Animation

Everybody knows that collapse is not looking cool without animation.
Everyone who little familiar with css, knows that for make animation you may use CSS3 Transitions.
For example:


.collapsing{
 height:80px; 
 overflow:hidden;
 transition:height 0.4s ease-in-out;
}

After applying this class on some element, its height will smoothly change to 80 pixels.

Trick

Animation will not work for elements those height is not specified (auto). So, for make some html elements to collapse with animation we must give them their own initial css height. One of the ways to measure the actual height of the element - is to use scrollHeight property.

    this.h = elem.scrollHeight;   


Lets use the trick on our the directive


import {Directive, Input, HostBinding, ElementRef} from 'angular2/core'
@Directive({selector: '[collapse]'})
export class Collapse { 

  @HostBinding('class.collapsing') 
  private isCollapsing:boolean   

    // style
  @HostBinding('style.height') 
  private height:string;
  
  
  @Input()
  private set collapse(value:boolean) { 
    if(value!==undefined){
      if(value){ 
        this.hide(); 
      }else { 
        this.show();
      }
    }
    //
  }
  constructor(public el: ElementRef) {
    
    this.measureHeight(); 
  }  
  measureHeight() {
    let elem = this.el.nativeElement;
    //lets be sure the element has display:block style
    elem.className = elem.className.replace('collapse', '');
    this.h = elem.scrollHeight;
   
  }
  hide(){
    this.height = this.h +'px'
    setTimeout(() => {
        this.height = '0px';
        this.isCollapsing = true;//apply 'collapsing' class
    },1); 
  }
  show() {
    this.height = '0px'
    setTimeout(() => {
        this.height = this.h + 'px';
        this.isCollapsing = true;//apply 'collapsing' class
    },1);
  }  
}

Here is running code: Hope you have fun reading...

1/23/16

creating chart component - angular2

After creating my first component which displays current time, i want to try something little bit complicate.
This time lets create a component which displays chart:

For draw a chart i will use jquery flot library which is one of libraries/plugins which let you to create beautiful charts.
According to jquery flot docs - for draw a chart you need to pass html element(selector), data and options to plot plugin, like this:

$.plot( selector, dataset, options);


Component

As in previous case, lets create component

import {Component, ElementRef} from 'angular2/core';

@Component({
  selector: 'flot',
  template: `<div>loading</div>`

})

export class FlotCmp {
  private dataset;
  private options;
  private width = '100%';
  private height = 220;

  
  constructor(public el: ElementRef) {
        this.dataset = [{label: "line1",data:  [[1, 130], [2, 40], [3, 80], [4, 160], [5, 159], [6, 370], [7, 330], [8, 350], [9, 370], [10, 400], [11, 330], [12, 350]]}];
        this.options = {
            series: {
                lines: { show: true },
                points: {
                    radius: 3,
                    show: true
                }
            }
        };
        
        let plotArea = $(this.el.nativeElement).find('div');
        plotArea.css({
            width: this.width,
            height: this.height
        });
        $.plot( plotArea, this.dataset, this.options);

  }//end of constructor
}

Yes!!! Now the chart s created!!!
Note that for access the html element on which i want to apply the chart, i'v used ElementRef module

Passing properties

Our next goal is to make newly created component configurable - e.g. ability to pass dataset, options, width, height configuration properties through the attributes:
  
     <flot [options]="splineOptions" [dataset]="dataset" height="550px" width="100%"></flot>
 
"Piece of cake!" you may say, like in previous case use the "input" factory and you done:

import {Component, ElementRef, Input} from 'angular2/core';

@Component({
  selector: 'flot',
  template: `<div>loading</div>`

})
//this will cause error!!!
export class FlotCmp{

  private width = '100%';
  private height = 220;
  static chosenInitialized = false;
  
  @Input() private  options: any;
  @Input() private  dataset:any;
  @Input() private  width:string;
  @Input() private  height:string;
      
   
  constructor(public el: ElementRef) {
        plotArea.css({
            width: this.width, 
            height: this.height
        });
        $.plot( plotArea, this.dataset, this.options);        
  }
  ...  

Bad idea! No chart created anymore.
But why? Because the constructor runs before input setters passing their values. We must move plot initiating logic to some later phase. I find the solution here. This guy uses AfterViewInit factory:

import {Component, ElementRef, Input, AfterViewInit} from 'angular2/core';

@Component({
  selector: 'flot',
  template: `<div>loading</div>`

})

export class FlotCmp  implements AfterViewInit{

  private width = '100%';
  private height = 220;
  static chosenInitialized = false;
  
  @Input() private  options: any;
  @Input() private  dataset:any;
  @Input() private  width:string;
  @Input() private  height:string;
 
  constructor(public el: ElementRef) {}

  ngAfterViewInit() {
      if(!FlotCmp.chosenInitialized) {
        let plotArea = $(this.el.nativeElement).find('div').empty();
        plotArea.css({
            width: this.width, 
            height: this.height
        });
        $.plot( plotArea, this.dataset, this.options);    
        FlotCmp.chosenInitialized = true;
      }
  } 
} 


Now the chart initiating logic is in the ngAfterViewInit method which activates after properies already set.
Hope you have fun reading!
More posts about angular2 stuff (used for me as inspiration source)

1/13/16

Building my first directive with angular2

Recently i started to play with recently released beta of angular2.
I'm totally beginner and i want to build very simple thing.
I want to build the component (component is like directive in angular 1.) that displays current time in format given as a parameter (through the attribute format).
  
  
    <now format="'h:mm:ss'"></now>
  
When rendered HTML, should look like this:

How to run angular2?

First thing you need then you want to run angular2 is environment.
You can use one of angular2 starter kits like this one Or just open plunker and select "Editor" -> "New" -> "AngularJs" -> "2.0.x TS" and viola!! you are angular2 programmer!


Component

The first basic thing in angular2 is component, so: Lets create a component:


import {Component} from 'angular2/core'

@Component({
  selector: 'now',
  template: `

{{date}}

` }) export class Now { private date; constructor() { this.date = new Date(); } }

Yoo hoo!!! The component created and running!!!
And it displays the current date!!!
But one little disturbing thing is that date appears raw , which is little bit ugly...

So lets improve the component by formatting the output.


Pipe (Filter)

Like in sngular 1. there are filters in angular2, they called pipes


import {Component} from 'angular2/core'

@Component({
  selector: 'now',
  template: `

{{date | date :'dd/MM/yyyy'}}

` })

Getting value from attribute (The tricky part)

As we already said the desired format should be passed through the attribute. But how to access this attribute from inside component constructor? Fortunately there is Attribute module which enables you to inject attribute into constructor parameters:

import {Component, Attribute} from 'angular2/core'
export class Now {
   private date;
   
  constructor(@Attribute("format") format) { 
    this.format = format;
    this.date =  new Date(); 

  } 

} 

Better way

After Lenin's older brother was executed because he tried(but failed) to kill king of Russia with bomb, young Lenin said "We will go different way"...
After looking at angular2 docs i found better (more angular2mish) way which says that if we want to pass attribute to component we must use square brackets :

  
  
    <now [format]="'h:mm:ss'"></now>
  

I will use @Input module instead of @attribute




import {Component,  Input} from 'angular2/core'

@Component({
  selector: 'now',
  template: `
      

{{date | date: _format}}

` }) export class Now { private date; @Input() set format(formatName:string){ this._format = formatName; } constructor() { this.date = new Date(); } }

How to update time each second?

Little nice thing to add here is ability to update the time displayed in directive each second. For achieve this i'm using ES6 fat arrow syntax


export class Now {
   private date;
   @Input() set format(formatName:string){
     this._format = formatName;
   
   }   
  constructor() { 
    this.date =  new Date(); 
    
    setInterval(() => {
        this.date =  new Date();
     }, 1000);
  } 

} 


View it on plunker:

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