<!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);
}
}