class: center, middle
--- # Initial setup 1. Install [Node.js](http://nodejs.org/) - This will also install npm. 1. Run `npm install -g bower gulp yo generator-ng-poly` - This enables Bower, Gulp, and Yeoman generators to be used from command line. 1. Run `npm install` to install this project's dependencies 1. Run `bower install` to install client-side dependencies 1. ??? 1. Profit! --- # Scaffolding the app Open a terminal, go to your desired folder and type:
yo ng-poly
Angular version: 1.4.*
Navigate to the created folder in your terminal and type:
yo ng-poly:directive songThubmnail
Will generate:
///<reference path='../../typings/tsd.d.ts' />
module SongThumbnail {
'use strict';
function SongThumbnailDirective(): ng.IDirective {
return {
restrict: 'EA',
scope: {},
templateUrl: 'home/song-thumbnail-directive.tpl.html',
replace: false,
controllerAs: 'songThumbnail',
controller: SongThumbnailController,
link: function (scope: ng.IScope, element: JQuery, attrs: any): void {
/*jshint unused:false */
/*eslint "no-unused-vars": [2, {"args": "none"}]*/
}
}
}
export class SongThumbnailController {
public name: string;
public static $inject: Array<string> = [];
constructor() {
this.name = 'songThumbnail';
}
}
angular
.module('home')
.directive('songThumbnail', SongThumbnailDirective);
}
<md-card class="song-thubmnail">
<md-card-header>
<md-card-header-text>
<span class="md-headline">{{song.artists[0].name}}</span>
<span class="md-subhead">{{song.artists[1].name}}</span>
</md-card-header-text>
</md-card-header>
<img ng-src="{{song.album.images[1].url}}" class="md-card-image" alt="Washed Out">
<md-button ng-show="!songThubmnail.isSongPlaying()" class="md-fab md-primary md-hue-2" ng-click="songThumbnail.playSong(song.preview_url)" aria-label="Profile">
<md-icon> play_arrow </md-icon>
</md-button>
<md-button ng-show="songThumbnail.isSongPlaying()" class="md-fab md-primary md-hue-2" ng-click="songThumbnail.stopPlaying()" aria-label="Profile">
<md-icon> pause </md-icon>
</md-button>
<md-card-title>
<md-card-title-text>
<span class="md-title">{{song.name}}</span>
</md-card-title-text>
</md-card-title>
</md-card>
Type:
yo ng-poly:service songFinder
and it will generate the base service.
Then, we can add our HTTP calls using $http service
///<reference path='../../typings/tsd.d.ts' />
module SongFinder {
'use strict';
export class SongFinder {
private $http: ng.IHttpService;
public static $inject: Array<string> = [
'$http',
];
constructor($http: ng.IHttpService) {
this.$http = $http;
}
public searchSong(songName: string): ng.IHttpPromise<any> {
return this.$http({
params: {
query: songName,
type: 'track'
},
method: 'GET',
url: 'https://api.spotify.com/v1/search'
})
}
}
/**
* @ngdoc service
* @name home.service:SongFinder
*
* @description
*
*/
angular
.module('home')
.service('SongFinder', SongFinder);
}
Creating the Song Interface
interface ISong {
artists: Array<{name: string}>,
album: { images: Array<{ url: string }> },
song: string,
}
Binding our new service:
private songFinder: SongFinder.SongFinder;
public songList: Array<ISong>;
// $inject annotation.
// It provides $injector with information about dependencies to be injected into constructor
// it is better to have it close to the constructor, because the parameters must match in count and type.
// See http://docs.angularjs.org/guide/di
public static $inject: Array<string> = [
'SongFinder'
];
// dependencies are injected via AngularJS $injector
constructor(SongFinder: SongFinder.SongFinder) {
this.songFinder = SongFinder;
}
Inside our controller:
private setSongList(songList: Array<ISong>): void {
this.songList = songList;
}
public searchSong(songName: string): void {
let vm: HomeCtrl = this;
if (songName) {
vm.songFinder.searchSong(songName).then(function (r: any): void {
vm.setSongList(r.data.tracks.items);
});
} else {
vm.setSongList(null);
}
}
<md-input-container>
<label>Search</label>
<input ng-model="home.songName" ng-change="home.searchSong(home.songName)" ng-model-options="{ debounce: 500 }">
</md-input-container>
///<reference path='../../typings/tsd.d.ts' />
module SongThumbnail {
'use strict';
export class SongThumbnailController {
public static $inject: Array<string> = [
'$rootScope'
];
public isPlaying: boolean;
private music: any;
private $rootScope: ng.IRootScopeService;
constructor($rootScope: ng.IRootScopeService) {
let vm: SongThumbnailController = this;
vm.$rootScope = $rootScope;
vm.music = undefined;
vm.$rootScope.$on('musicPlaying', function(): void {
if (vm.isSongPlaying()) {
vm.stopPlaying();
}
});
}
public playSong(previewUrl: string): void {
this.emitPlayingEvent();
this.music = new Audio(previewUrl);
this.music.play();
}
public stopPlaying(): void {
if (this.isSongPlaying()) {
this.music.pause();
this.music = undefined;
}
}
private emitPlayingEvent(): void {
this.$rootScope.$broadcast('musicPlaying');
}
public isSongPlaying(): boolean {
return this.music !== undefined;
}
}
}