Last active
July 3, 2016 23:23
-
-
Save willSonic/474d063e4f8633ccc3337b353939ed21 to your computer and use it in GitHub Desktop.
Pieces of my plunker http://plnkr.co/edit/RLKNdm?p=preview that I am using to help answer questions about notifying a web component of a state change
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'rxjs/add/operator/catch'; | |
import 'rxjs/add/operator/map'; | |
import 'rxjs/add/operator/mapTo'; | |
import 'rxjs/add/operator/filter'; | |
import 'rxjs/add/operator/mergeMap'; | |
import 'rxjs/add/operator/switchMap'; | |
import 'rxjs/add/operator/switchMapTo'; | |
import 'rxjs/add/operator/toArray'; | |
import 'rxjs/add/observable/of'; | |
import { Injectable } from '@angular/core'; | |
import { Store } from '@ngrx/store'; | |
import { Effect, StateUpdates, toPayload } from '@ngrx/effects'; | |
import { Observable } from 'rxjs/Observable'; | |
import { AppState, AudioAlbumState } from '../reducers'; | |
import { AudioItemServices } from '../services/audioItemServices'; | |
import { WebAudioServices } from '../services/webAudioServices'; | |
import { ArtistService } from '../services/artistService'; | |
import { AudioArtistActions} from '../actions/audioArtistActions'; | |
import { AudioItemActions} from '../actions/audioItemActions'; | |
import { AudioAlbumActions} from '../actions/audioAlbumActions'; | |
import { AudioArtist, AudioItem} from '../models'; | |
@Injectable() | |
export class AudioArtistEffects { | |
constructor( | |
private updates$: StateUpdates<AppState>, | |
private audioItemServices:AudioItemServices, | |
private artistService: ArtistService, | |
private webAudioServices:WebAudioServices; | |
private audioItemActions: AudioItemActions, | |
private audioArtistActions: AudioArtistActions, | |
private audioAlbumActions:AudioAlbumActions | |
) { } | |
@Effect() loadAudioArtistsInit$ = Observable.of(this.audioArtistActions.fetchAudioArtist()); | |
@Effect() fetchAudioArtists = this.updates$ | |
.whenAction(AudioArtistActions.FETCH_AUDIO_ARTIST) | |
.mergeMap(() => this.artistService.requestArtist()) | |
.map((audioArtists:AudioArtist[]) => this.audioArtistActions.fetchAudioArtistComplete(audioArtists)) | |
.catch(() => Observable.of(this.audioArtistActions.fetchAudioArtistComplete([]) )); | |
@Effect() createAudioAlbums = this.updates$ | |
.whenAction(AudioArtistActions.AUDIO_ARTIST_FETCH_COMPLETE) | |
.map<AudioArtist[]>(toPayload) | |
.map((audioArtists:AudioArtist[]) => this.audioAlbumActions.createAudioAlbumList(audioArtists)); | |
@Effect() createAudioItems = this.updates$ | |
.whenAction(AudioAlbumActions.CREATE_AUDIOALBUM_LIST) | |
.mergeMap(() =>this.audioItemServices.getAudioItems()) | |
.map((audioAlbums:AudioAlbum[]) => this.audioItemActions.createAudioItemList(audioAlbums)); | |
@Effect() downLoadAudioItem = this.updates$ | |
.whenAction(AudioItemActions.BEGIN_AUDIOITEM_DOWNLOAD) | |
.map<AudioItem>(toPayload) | |
.mergeMap((audioItem:AudioItem) => this.webAudioServices.downLoadAudioItem(audioItem)) | |
.map(audioItem => this.audioItemActions.audioItemDownLoadComplete(audioItem)) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, Input,Output, EventEmitter } from '@angular/core'; | |
import { AudioItemViewComponent, AudiItemInput } from './audioItemViewComponent'; | |
export type AudioItemsInput = AudioItemInput[]; | |
@Component({ | |
selector: 'audioitems-list', | |
directives: [ AudioItemViewComponent ], | |
template: ` | |
<audioitem-view | |
(changeState) = "changeState.emit($event)" | |
*ngFor="let audioItem of audioItems" [audioItem]="audioItem"></audioitem-view> | |
`, | |
styles: [` | |
.demo-list-icon { | |
width: 400px; | |
} | |
`] | |
}) | |
export class AudioItemListComponent { | |
@Input() audioItems: AudioItemsInput; | |
@Output() changeState = new EventEmitter<Album>(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import '@ngrx/core/add/operator/select'; | |
import 'rxjs/add/operator/map'; | |
import { Observable } from 'rxjs/Observable'; | |
import { Action } from '@ngrx/store'; | |
import { uuid } from '../utils/uuid'; | |
import { AudioAlbum } from '../models/index'; | |
import { AudioItemActions } from '../actions/audioItemActions'; | |
export interface AudioItemState { | |
ids: string[]; | |
entities: { [id: string]: AudioItem }; | |
}; | |
const initialState: AudioItemState = { | |
ids: [], | |
entities: {} | |
}; | |
export default function(state = initialState, action: Action): AudioItemState { | |
switch (action.type) { | |
case AudioItemActions.CREATE_AUDIOITEM_LIST:{ | |
const audioAlbums:AudioAlbum[] = action.payload; | |
const audioItems:AudioItems[] = audioAlbums.map(audioAlbum => { | |
return Object.assign( {}, {id:uuid(), | |
audioAlbumId:audioAlbum.id, | |
albumImgSrc: audioAlbum.albumImgSrc, | |
trackURL:audioAlbum.trackURL, | |
artistAudioBuffer:null, | |
loadProgress:0; | |
downloadComplete:false, | |
isPlaying:false, | |
currentPosition:0})}); | |
const newAudioItemIds = audioItems.map(audioItem => audioItem.id); | |
const newAudioItemEntities = audioItems.reduce((entities: { [id: string]: AudioItem }, audioItem: AudioItem) => { | |
return Object.assign(entities, { | |
[audioItem.id]: audioItem | |
}); | |
}, {}); | |
return { | |
ids: [ ...state.ids, ...newAudioItemIds ], | |
entities: Object.assign({}, state.entities, newAudioItemEntities) | |
}; | |
} | |
case AudioItemActions.BEGIN_AUDIOITEM_DOWNLOAD:{ | |
const audioItem: AudioItem = action.payload; | |
// console.log('[audioItemReducer.ts]--- BEGIN_AUDIOITEM_DOWNLOAD--- audioItem',audioItem); | |
if (state.ids.includes(audioItem.id)) { | |
return state; | |
} | |
} | |
case AudioItemActions.PROGRESS_OF_AUDIOITEM_DOWNLOAD:{ | |
const audioItem: AudioItem = action.payload; | |
if (state.ids.includes(audioItem.id)) { | |
console.log('[audioItemReducer.ts]--- PROGRESS_OF_AUDIOITEM_DOWNLOAD--- audioItem.loadProgress='+audioItem.loadProgress); | |
return { | |
ids: [ ...state.ids], | |
entities: Object.assign({}, state.entities, { [audioItem.id]: audioItem}) | |
}; | |
} | |
} | |
case AudioItemActions.AUDIOITEM_DOWNLOAD_COMPLETE:{ | |
const audioItem: AudioItem = action.payload; | |
if (state.ids.includes(audioItem.id)) { | |
console.log('[audioItemReducer.ts]--- AUDIOITEM_DOWNLOAD_COMPLETE--- audioItem = ', audioItem); | |
return { | |
ids: [ ...state.ids], | |
entities: Object.assign({}, state.entities, { [audioItem.id]: audioItem}) | |
}; | |
} | |
} | |
default: { | |
return state; | |
} | |
} | |
} | |
export function getAudioItemEntities() { | |
return (state$: Observable<AudioItemState>) => state$ | |
.select(s => s.entities); | |
}; | |
export function getAudioItemIds() { | |
return (state$: Observable<AudioItemState>) => state$ | |
.select(s => s.ids); | |
}; | |
export function getAudioItem(id: string) { | |
return (state$: Observable<AudioItemState>) => state$ | |
.select(s => s.entities[id]); | |
}; | |
export function getAudioItems(audioItemIds: string[]) { | |
return (state$: Observable<AudioItemState>) => state$ | |
.let(getAudioItemEntities()) | |
.map(entities => audioItemIds.map(id => entities[id])); | |
} | |
export function hasAudioItem(id: string) { | |
return (state$: Observable<AudioItemState>) => state$ | |
.select(s => s.ids.includes(id)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Component, ChangeDetectionStrategy, Output, Input, SimpleChange, OnInit, EventEmitter, ChangeDetectorRef,OnChanges } from "@angular/core"; | |
import { AudioItem } from "../models/audio-item-model"; | |
import { Observable } from 'rxjs/Observable'; | |
export type AudioItemInput = AudioItem; | |
export type ChangeStateOutput = AudioItem; | |
@Component({ | |
selector: 'audioitem-view', | |
inputs: ['audioItem'], | |
styles: [` | |
.mdl-list__item-icon, .mdl-list__item-icon.material-icons{ | |
color:#7db500; | |
} | |
.mdl-list__item-primary-content{ | |
color:#65788b; | |
} | |
img { | |
width: 64px; | |
height:64px; | |
margin-left: 2px; | |
} | |
label.base{ | |
color:#7db500; | |
} | |
label.loading{ | |
color:#E54028; | |
} | |
label.loaded{ | |
color:#4c78a4; | |
} | |
.mdl-button--raised.mdl-button--colored:hover, | |
.mdl-button--raised.mdl-button--colored { | |
background-color: #7F8D9E; | |
} | |
} | |
`], | |
template: ` | |
<li class="mdl-list__item"> | |
<span class="mdl-list__item-primary-content"> | |
<img [src]="thumbnail"/> | |
</span> | |
<div class="mdl-cell mdl-cell--4-col"> | |
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" | |
(click)="changeState.emit(audioItem)"> | |
{{buttonLabel}} | |
</button> | |
</div> | |
<div class="mdl-cell mdl-cell--4-col"> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<label class="mdl-textfield__label" [ngClass]="setClasses()"> {{audioItemState}}</label> | |
</div> | |
</div> | |
</li> | |
` | |
changeDetection: ChangeDetectionStrategy.OnPush | |
}) | |
export class AudioItemViewComponent implements onChanges{ | |
@Input('audioItem') _audioItem: AudioItemInput; | |
audioItem:AudioItemInput; | |
@Output() changeState: EventEmitter<AudioItem> = new EventEmitter<AudioItem>(); | |
buttonLabel:string; | |
audioItemState:String; | |
set _audioItem(value:AudioItem){ | |
this.audioItem = Object.assign({}, value); | |
this.setClasses(); | |
} | |
get thumbnail():string{ | |
return this.audioItem.albumImgSrc; | |
} | |
downloadComplete(){ | |
this.buttonLabel = (this.audioItem.downloadComplete)? "LOADING":"LOAD"; | |
this.audioItemState = (this.audioItem.loadProgress==0)? "...":this.audioItem.loadProgress; | |
return this.audioItem.downloadComplete; | |
} | |
isPlaying(){ | |
if(this.audioItem.downloadComplete){ | |
this.buttonLabel = (this.audioItem.isPlaying)? "STOP":"PLAY"; | |
this.audioItemState = 'position :'+this.audioItem.currentPosition; | |
} | |
return this.audioItem.isPlaying; | |
} | |
setClasses() { | |
let classes = { | |
base: (this.downloadComplete() == false), | |
loading: (this.downloadComplete() == true), | |
loader: (this.isPlaying() == true) | |
}; | |
return classes; | |
} | |
ngOnChanges(changes: {[propName: string]: SimpleChange}): void { | |
console.log('[AudioItemViewComponent&&&&&&&&&Changes', changes); | |
console.log('[AudioItemViewComponent]************this.audioItems', this.audioItem); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { provideStore, Store } from '@ngrx/store'; | |
import { provide, Input, Directive,HostBinding } from '@angular/core'; | |
import { runEffects } from '@ngrx/effects'; | |
import { Component, Injectable, Output, Input} from '@angular/core'; | |
import { AudioItemServices } from './services/audioItemServices'; | |
import { AppState,AudioItemState, getAudioArtists, getAudioAlbums, getAudioItems, getAudioItemState } from './reducers/index'; | |
import { AudioArtistListComponent, AudioArtistsInput } from './components/audioArtistListComponent'; | |
import { AudioAlbumListComponent, AudioAlbumsInput } from './components/audioAlbumListComponent'; | |
import { AudioItemListComponent, AudioItemsInput } from './components/audioItemListComponent'; | |
@Component({ | |
selector: 'app', | |
directives: [ | |
AudioArtistListComponent, | |
AudioAlbumListComponent, | |
AudioItemListComponent | |
], | |
styles:[` | |
*{font-family: Monaco, Consolas;} | |
a{ | |
font-size: 9pt; | |
color: black; | |
text-decoration: none; | |
padding: 2px; | |
} | |
a:hover{color: blue;} | |
a::before{content: ">";} | |
.list{ | |
display: flex; | |
flex-direction: column; | |
} | |
.headline{ | |
color:#2aa4c9; | |
padding-left:10px; | |
} | |
`], | |
template: ` | |
<div mdl class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header"> | |
<header class="demo-header mdl-layout__header mdl-color--grey-100 mdl-color-text--grey-600"> | |
<div class="mdl-layout__header-row"> | |
<span class="mdl-layout-title">NGRX Harness For Plunker</span> | |
</div> | |
</header> | |
<main class="mdl-layout__content mdl-color--grey-12"> | |
<div class="mdl-grid"> | |
<div class="demo-graphs mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--5-col"> | |
<h5 class="headline"> List of Artists</h5> | |
<audioartist-list [audioArtists]="audioArtists$ | async" | |
class="demo-list-icon mdl-list"></audioartist-list> | |
</div> | |
<div class="demo-graphs mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--5-col"> | |
<h5 class="headline">List of Albums</h5> | |
<audioalbum-list [audioAlbums]="audioAlbums$ | async" | |
class="demo-list-icon mdl-list"></audioalbum-list> | |
</div> | |
<div class="demo-graphs mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--5-col"> | |
<h5 class="headline">List of Audio Items</h5> | |
<audioitems-list [audioItems]="audioItems$ | async" | |
(changeState)="changeStateOfAudioItem($event)" | |
class="demo-list-icon mdl-list"></audioitems-list> | |
</div> | |
</div> | |
</main> | |
</div> | |
` | |
}) | |
export default class App{ | |
audioArtists$: Observable<AudioArtistsInput>; | |
audioAlbums$: Observable<AudioAlbumsInput>; | |
audioItems$: Observable<AudioItemsInput>; | |
audioItemState$: Observable<AudioItemState>; | |
constructor(private store: Store<AppState>, private audioItemServices:AudioItemServices) { | |
this.audioArtists$ = store.let(getAudioArtists()); | |
this.audioAlbums$ = store.let(getAudioAlbums()); | |
this.audioItems$ = store.let(getAudioItems()); | |
this.audioItemState$ = store.let(getAudioItemState()); | |
this.audioItems$.subscribe(state =>{ | |
console.log("[Main.ts] ----App--- this.audioItems$.subscribe state =",state); | |
}); | |
} | |
changeStateOfAudioItem(audioItem:AudioItem){ | |
this.audioItemServices.updateAudioItemState(audioItem); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Injectable, bind} from '@angular/core'; | |
import {Http, BrowserXHhr} from "@angular/http"; | |
import {Subject} from "rxjs/Subject"; | |
import {Observable} from 'rxjs/Observable'; | |
import {AudioItem} from "../models/audio-item-model"; | |
import {Store, State, Action} from '@ngrx/store'; | |
import {Subject} from "rxjs/Subject"; | |
import {Observable} from 'rxjs/Observable'; | |
import {AudioItemActions} from '../actions/audioItemActions'; | |
@Injectable() | |
export class WebAudioServices{ | |
private audioContext: AudioContext; | |
private audioNode:AudioBufferSourceNode; | |
private audioBuffer: AudioBuffer; | |
private playbackRate: number = 1.0; | |
private gainNode:GainNode; | |
private gain: number = .01; | |
constructor( private _store: Store, private audioItemActions:AudioItemActions) { | |
this.audioContext = new AudioContext(); | |
} | |
downLoadAudioItem(audioItem:AudioItem): Observable<any> { | |
console.log("[WebAudioServices]---- audioItem =", audioItem); | |
let storeRef = this._store; | |
return Observable.create(observer=> { | |
let req = new XMLHttpRequest(); | |
req.open('GET', audioItem.trackURL, true ); | |
req.responseType = "arraybuffer"; | |
req.onreadystatechange = function () { | |
if (req.readyState == 4 && req.status == 200) { | |
audioItem.artistAudioBuffer = req.response; | |
audioItem.downloadComplete = true; | |
observer.next(audioItem); | |
observer.complete(); | |
} | |
}; | |
req.onprogress = function(evt) | |
{ | |
console.log("[WebAudioServices] loadAudio -- req.onprogress-- =", evt); | |
if (evt.lengthComputable) | |
{ //evt.loaded the bytes browser receive | |
//evt.total the total bytes seted by the header | |
// | |
var percentComplete = (evt.loaded / evt.total) * 100; | |
audioItem.loadProgress = (evt.loaded / evt.total) * 100; | |
storeRef.dispatch({type:AudioItemActions.PROGRESS_OF_AUDIOITEM_DOWNLOAD , payload:audioItem }); | |
} | |
}; | |
req.send(); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment