<!DOCTYPE html>
<html>

  <head>
    <title>Angular 2 Accordion modified</title>
    <script src="https://unpkg.com/systemjs@0.19.38/dist/system.src.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="system.config.js"></script>
  </head>

  <body>
    <app-root>Loading...</app-root>
    <script>
      System.import('app');
    </script>
  </body>

</html>
#Angular 2 Accordion
var angularVersion = '2.4.6';

System.config({
  baseUrl: '/',
  paths: {
    'unpkg:*': 'https://unpkg.com/*'
  }
});

System.config({
  transpiler: 'typescript', 
  typescriptOptions: { emitDecoratorMetadata: true },

  meta: {
    '*': {
      deps: [ 'zone.js', 'reflect-metadata' ]
    }
  }
});

System.config({
  packageConfigPaths: [
    "unpkg:@*/*/package.json"
  ],
  
  map: {
    '@angular/core': 'unpkg:@angular/core@'+angularVersion,
    '@angular/compiler': 'unpkg:@angular/compiler@'+angularVersion,
    '@angular/common': 'unpkg:@angular/common@'+angularVersion,
    '@angular/platform-browser': 'unpkg:@angular/platform-browser@'+angularVersion,
    '@angular/platform-browser-dynamic': 'unpkg:@angular/platform-browser-dynamic@'+angularVersion,
    '@angular/http': 'unpkg:@angular/http@'+angularVersion,
    '@angular/forms': 'unpkg:@angular/forms@'+angularVersion,
    '@angular/router-deprecated': 'unpkg:@angular/router-deprecated@'+angularVersion,
    'immutable': 'unpkg:immutable@3.8.1',
    'redux': 'https://unpkg.com/redux@2.0.0/dist/redux.js',
    'ng2-redux': 'unpkg:ng2-redux@2.3.2',
    'lodash': 'unpkg:lodash@4.0.0',
    'invariant': 'unpkg:invariant@2.2.1',
    'redux-thunk': 'unpkg:redux-thunk@2.1.0',
    'redux-logger': 'unpkg:redux-logger@2.6.0',
    'rxjs': 'unpkg:rxjs@5.0.0-beta.12',
    'zone.js': 'unpkg:zone.js@0.6.25',
    'reflect-metadata': 'unpkg:reflect-metadata@0.1.3',
    "crypto": "@empty"
  },
  
  packages: {
    'app': {
      defaultExtension: 'ts',
      main: './main.ts'
    }
  }
});
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { bootstrap, platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { Accordion } from './accordion.component';
import { AccordionGroup } from './accordion-group.component';

import { PostsService } from './posts.service';


@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    HttpModule
  ],
  declarations: [
    AppComponent,
    Accordion,
    AccordionGroup
  ],
  providers: [ PostsService ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}

platformBrowserDynamic().bootstrapModule(AppModule);
import { Component, OnInit, OnDestroy } from '@angular/core';
import { PostsService } from './posts.service';


@Component({
  selector: 'app-root',
  template: `
    <tp-accordion-group>
      <tp-accordion *ngFor="let post of posts" [title]="post.title">
        {{ post.body }}
      </tp-accordion>
    </tp-accordion-group>
  `
})
export class AppComponent implements OnInit, OnDestroy {
  
  posts = [];
  private subscription: any;

  constructor(private postsSvc: PostsService) {}
  
  ngOnInit() {
    this.subscription = this.postsSvc.getPosts().subscribe(res => {
      if (res.length) {
        this.posts = res.slice(0, 10);
      }
    })
  }
  
  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }

}
import { Component, ContentChildren, QueryList, AfterContentInit, OnDestroy } from '@angular/core';

import { Accordion } from './accordion.component';

@Component({
  selector: 'tp-accordion-group',
  template: `
    <ng-content></ng-content>
  `
})
export class AccordionGroup {
  
  @ContentChildren(Accordion) accordions: QueryList<Accordion>;
  private subscriptions = [];
  
  private _accordions = [];

  constructor() {}
  
  ngAfterContentInit() {
    
    this._accordions = this.accordions;
    this.removeSubscriptions();
    this.addSubscriptions();
    
    this.accordions.changes.subscribe(rex => {
      this._accordions = rex;
      this.removeSubscriptions();
      this.addSubscriptions();
    });
  }
  
  addSubscriptions() {
    this._accordions.forEach(a => {
      let subscription = a.toggleAccordion.subscribe(e => {
        this.toogleAccordion(a);
      });
      this.subscriptions.push(subscription);
    });
  }
  
  removeSubscriptions() {
    this.subscriptions.forEach(sub => {
      sub.unsubscribe();
    });
  }
  
  toogleAccordion(accordion) {
    if (!accordion.active) {
      this.accordions.forEach(a => a.active = false);
    }
    // set active accordion
    accordion.active = !accordion.active;
  }
  
  ngOnDestroy() {
    this.removeSubscriptions();
  }

}
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'tp-accordion',
  template: `
    <h2 class="accordion-head" (click)="onClick($event)">{{ title }}</h2>
    <div class="accordion-body" [class.active]="active">
      <ng-content></ng-content>
    </div>
  `,
  styles: [
    `
    .accordion-head {
      cursor: pointer;
    }
    .accordion-body {
      display: none;
    }
    .accordion-body.active {
      display: block;
      -webkit-animation: fadeIn .3s;
      animation: fadeIn .3s;
    }
    @-webkit-keyframes fadeIn {
      from { opacity: 0; transform: scale(0); }
        to { opacity: 1; transform: scale(1); }
    }  
    @keyframes fadeIn {
      from { opacity: 0; transform: scale(0); }
        to { opacity: 1; transform: scale(1); }
    }
    `  
  ],
})
export class Accordion {
  
  @Input() title: string;
  
  @Input() active: boolean = false;
  
  @Output() toggleAccordion: EventEmitter<boolean> = new EventEmitter();

  constructor() {}
  
  onClick(event) {
    event.preventDefault();
    this.toggleAccordion.emit(this.active);
  }

}
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class PostsService {
  postsUrl: 'https://jsonplaceholder.typicode.com/posts';
  constructor(private http: Http) {
    
  }
  getPosts() {
    return this.http.get(this.postsUrl)
      .map(res => {
        let body = res.json();
        return body || [];
      })
      .catch(console.log);
  }
}