Angular Google Maps, Directive inside Info Window isn't binding data - angularjs

I'm using Angular google Maps trying to get an info window working. When a marker is selected, the window is placed there and shown. Its content is some text and also some directives. The directives inside even work, except for when I try to bind some data its attributes.
In this case I'm having a rating directive (shows a bunch of stars) that takes a number attribute, as so:
<div stars number="person.rating"></div>
Note that this directives works just fine in a couple of other places in my app. Also, when I replace person.rating' for3, the directive renders perfectly. when I useperson.rating` outside of this div, it returns a number as expected.
Only when I combine the person.rating inside the number attribute, inside the window directive, do things go awry:
<ui-gmap-google-map center='map.center' class="big-maps" options='map.options' events="map.events" zoom='map.zoom'>
<ui-gmap-window coords="selectedCoords" show="windowOptions.visible" closeClick="closeWindow">
<div>
Name: {{person.firstName}}, Rating: {{person.rating}} <!-- these work just fine. -->
<div> So here we are. {{person.rating}}</div> <!-- this also works just fine. -->
<div stars number="3"></div> <!-- Even this works fine. -->
<div stars number="person.rating"></div> <!-- But this doesn't... -->
</div>
</ui-gmap-window>
<ui-gmap-markers
models='markers'
idKey='id'
coords="'coords'"
icon="'icon'"
options="'options'"
doCluster="false"
click="showWindow"
>
</ui-gmap-markers>
</ui-gmap-google-map>
The directive:
ndtApp.directive('stars', stars);
function stars() {
return {
restrict: "AE",
replace: 'true',
scope: {
number: "=",
size: "#"
},
templateUrl: "shared/stars/stars.html",
controller: controller
};
}
function controller($scope){
$scope.getEmptyStarsArray = getEmptyStarsArray;
$scope.getStarsArray = getStarsArray;
$scope.ratingSize = "";
activate();
function activate(){
if ($scope.size == 'sm'){
$scope.ratingSize = 'rating-sm';
} else if ($scope.size == 'lg'){
$scope.ratingSize = 'rating-lg';
}
console.log($scope.number);
}
function getEmptyStarsArray(){
if ($scope.number === undefined) return 0;
var rating = Math.round($scope.number);
if (rating > 4) return null;
return new Array(5 - rating);
}
function getStarsArray(){
if ($scope.number === undefined) return 0;
var rating = Math.round($scope.number);
if (rating === 0) return null;
if (rating > 5) return new Array(5);
return new Array(rating);
}
}
stars.html:
<div class="stars">
<span ng-repeat="i in getStarsArray() track by $index" class="{{ratingSize}} rating glyphicon glyphicon-star"></span><span ng-repeat="i in getEmptyStarsArray() track by $index" class="{{ratingSize}} rating glyphicon glyphicon-star-empty"></span>
</div>
Any help is highly appreciated!

It seems directive scope property is not getting bound, try to change scope for number property from number: "=" to number: "#" and directive initialization from <div stars number="person.rating"></div> to <div stars number="{{person.rating}}"></div>
Below is provided a working similar example:
var appMaps = angular.module('appMaps', ['uiGmapgoogle-maps']);
appMaps.controller('mainCtrl', function($scope,uiGmapIsReady) {
$scope.map = {
center: { latitude: 40.1451, longitude: -99.6680 },
zoom: 4,
options: { scrollwheel: false }
};
$scope.windowOptions = { visible: false };
var getRandomLat = function() {
return Math.random() * (90.0 + 90.0) - 90.0;
};
var getRandomLng = function () {
return Math.random() * (180.0 + 180.0) - 180.0;
};
var getRandomRating = function() {
return Math.floor(Math.random() * 11);
};
var createRandomMarker = function(i) {
var ret = {
latitude: getRandomLat(),
longitude: getRandomLng(),
title: 'Hotel #' + i,
show: false,
id: i
};
return ret;
};
$scope.onClick = function(marker, eventName, model) {
$scope.windowOptions.visible = true;
$scope.selectedHotel = model;
$scope.rating = { number: getRandomRating()};
};
$scope.closeWindow = function () {
$scope.windowOptions.visible = false;
};
$scope.hotels = [];
for (var i = 0; i < 256; i++) {
$scope.hotels.push(createRandomMarker(i));
}
});
appMaps.directive('rating',
function() {
return {
restrict: "AE",
replace: true,
scope: {
//number: "=",
number: "#"
},
controller: ratingController,
//template: '<div >Rating: {{ ::number }}</div>',
template: function(tElem, tAttrs){
return '<div class="rating"> Rating:' + tAttrs.number + '</div>';
}
};
});
function ratingController($scope){
console.log($scope.number);
}
html, body, #map_canvas {
height: 100%;
width: 100%;
margin: 0px;
}
#map_canvas {
position: relative;
}
.angular-google-map-container {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
<script src="https://code.angularjs.org/1.3.14/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
<script src="http://rawgit.com/angular-ui/angular-google-maps/2.0.X/dist/angular-google-maps.js"></script>
<div id="map_canvas" ng-app="appMaps" ng-controller="mainCtrl">
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="true" options="options" events="map.events">
<ui-gmap-window coords="selectedHotel" show="windowOptions.visible" closeclick="closeWindow">
<div>
<div>{{selectedHotel.title}}</div>
<div rating number="{{rating.number}}"></div>
</div>
</ui-gmap-window>
<ui-gmap-markers models="hotels" coords="'self'" icon="'icon'" click="onClick">
</ui-gmap-markers>
</ui-gmap-google-map>
</div>

Related

How to Create playlist in angularjs audio app?

I had created a angular audio app. i want to display play list in it . Using directive to trigger play pause and other operations. Please help me to create play list here.
my main html is
<!DOCTYPE html>
<html ng-app="app">
<head>
<title> AUDIO</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" type="text/css">
<link rel="stylesheet" href="css/ng-cool-audio.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
<script src="js/ng-cool-audio.js"></script>
<script src="js/app.js"></script>
<style>
body {
background-color: #f5f5f5;
font-family: Helvetica Neue, Helvetica, arial, sans-serif;
}
.main {
width: 800px;
margin: 100px auto;
}
</style>
</head>
<body ng-controller="MainCtrl">
<div class="main">
<ng-cool-audio source="source2"></ng-cool-audio>
</div>
</body>
</html>
my html template is
<div class="ng-cool-audio-container">
<audio>
Your browser seems to be upgraded! :)
</audio>
<div class="ng-cool-audio-preview">
<div class="ncv-audio-left">
<div class="ncv-img-container">
<img ng-src="{{audio.cover}}" alt="Photo">
</div>
</div>
<div class="ncv-audio-right">
<div class="ncv-audio-right-top">
<div class="ncv-header">
<div class="ncv-header-title">{{audio.author}}</div>
<div class="ncv-header-subtitle">{{audio.name}}</div>
<div class="ncv-audio-sound">
<i class="fa fa-volume-up" ng-show="!isMuted" ng-click="volumeOff()"></i>
<i class="fa fa-volume-off" ng-show="isMuted" ng-click="volumeOn()"></i>
<input type="range" max="10" min="0" value="5">
</div>
</div>
</div>
<div class="ncv-audio-right-bottom">
<div class="ncv-audio-controls">
<i class="fa fa-backward" ng-click="playBackward()"></i>
<i class="fa fa-play" ng-click="play()"></i>
<i class="fa fa-pause" ng-click="pause()"></i>
<i class="fa fa-forward" ng-click="playForward()"></i>
</div>
<div class="ncv-progress">
<progress class="ncv-progress-bar" max="100" value="{{progressValue}}"></progress>
</div>
<div class="ncv-time">
<span class="ncv-current-time">{{currentTime}}</span>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<h4> PLAY LIST </h4>
<div class="col-lg-3" ng-repeat="song in source2">
{{song.audio.name}}
</div>
</div>
</div>
app.js is
(function() {
'use strict';
angular
.module('app', [
'ngCoolAudio'
])
.controller('MainCtrl', function($scope) {
$scope.source2 = {
audio: [{
author: 'Dr. SPB',
cover: 'images/about.jpg',
src: 'audio/AADI ANAADI-SPB.mp3',
name: 'AADI ANAADI'
},
{
author: 'Dr.RAJ',
cover: 'images/about.jpg',
src: 'audio/BHAVA SAAGARADA AALADI-DR.RAJ.mp3',
name: 'BHAVA SAAGARADA AALADI'
},
{
author: 'Dr.RAJ',
cover: 'images/about.jpg',
src: 'audio/CHARAKULAAMBUDHI CHANDIRA-DR.RAJ (2).mp3',
name: 'CHARAKULAAMBUDHI CHANDIRA'
},
{
author: 'S.JANAKI',
cover: 'images/about.jpg',
src: 'audio/DHYANA MAADUTHIRU-S.JANAKI .mp3',
name: 'DHYANA MAADUTHIRU'
},
{
author: 'OM',
cover: 'images/about.jpg',
src: 'audio/Om Namahshivaya.mp3',
name: 'OM NAMAHSHIVAYA'
}
],
config: {
autoplay: false,
loop: true
}
}
})
})();
audio directive is
(function() {
'use strict';
angular
.module('ngCoolAudio', [])
.directive('ngCoolAudio', [
'$timeout',
'$sce',
function($timeout, $sce) {
return {
restrict: 'AE',
scope: {
source: '=source'
},
replace: true,
templateUrl: 'html/audio.html',
controller: ['$scope', '$element', function($scope, $element) {
//check source file
if (!$scope.source || !$scope.source.audio) {
throw new Error('Source seems not to config right!');
return;
}
var container = $element[0].querySelector('.ng-cool-audio-container');
var audio = $element[0].querySelector('audio');
var volume_range = $element[0].querySelector('.ncv-audio-sound input[type="range"]');
var $audio = angular.element(audio);
$scope.audio = {};
//private method
var calCulateTime = function() {
var secs = parseInt(audio.currentTime % 60);
var mins = parseInt((audio.currentTime / 60) % 60);
// Ensure it's two digits. For example, 03 rather than 3.
secs = ("0" + secs).slice(-2);
mins = ("0" + mins).slice(-2);
return mins + ':' + secs;
}
var calCulateProgress = function() {
var percent = (100 / audio.duration) * audio.currentTime;
return percent;
}
var generateAudio = function(audio) {
if (!audio.src) {
throw new Error('Not found src in your audio config');
return;
}
$audio.attr('src', audio.src);
$scope.audio.cover = audio.cover || 'http://7xj610.com1.z0.glb.clouddn.com/29ce98b4349b72c2778d2f82823159b06f98f8bc.jpeg';
$scope.audio.author = audio.author || 'Unknow';
$scope.audio.name = audio.name || 'Unknow';
}
$scope.currentTrack = 0;
$scope.jumpInterval = 10;
$scope.init = function() {
$scope.currentTime = '00:00';
$scope.progressValue = 0;
$scope.isPlaying = false;
$scope.isMuted = false;
$scope.setInterface($scope.currentTrack);
$scope.addEvents();
};
$scope.setInterface = function(index) {
var isArray = angular.isArray($scope.source.audio);
if (isArray) {
$scope.audioCollection = $scope.source.audio;
generateAudio($scope.audioCollection[index]);
} else {
generateAudio($scope.source.audio);
}
if ($scope.source.config) {
if ($scope.source.config.autoplay) {
$audio.attr('autoplay', 'autoplay');
$scope.play();
}
if ($scope.source.config.loop) {
$audio.attr('loop', 'loop');
}
}
};
//toggle play pause
$scope.play = function() {
audio.play();
$scope.isPlaying = true;
};
$scope.pause = function() {
audio.pause();
$scope.isPlaying = false;
};
//toggle mute
$scope.volumeOn = function() {
audio.muted = false;
$scope.isMuted = false;
};
$scope.volumeOff = function() {
audio.muted = true;
$scope.isMuted = true;
};
//backward forward
$scope.playBackward = function() {
//here jump to pre song
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.currentTrack -= 1;
if ($scope.currentTrack < 0) {
$scope.currentTrack = $scope.audioCollection.length - 1;
}
$scope.init();
$scope.play();
} else {
var toTime = audio.currentTime - $scope.jumpInterval;
if (toTime < 0) {
audio.currentTime = 0;
} else {
audio.currentTime = toTime;
}
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
};
$scope.playForward = function() {
//here jump to next song
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.currentTrack += 1;
if ($scope.currentTrack > $scope.audioCollection.length - 1) {
$scope.currentTrack = 0;
}
$scope.init();
$scope.play();
} else {
var toTime = audio.currentTime + $scope.jumpInterval;
if (toTime > audio.duration) {
audio.currentTime = audio.duration;
} else {
audio.currentTime = toTime;
}
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
};
//skip progress
$scope.skipProgress = function(e) {
//update time and progress
var target = e.target;
var offsetX = 0;
if (!e.pageX || !target.offsetLeft) {
offsetX = e.offsetX ? e.offsetX : e.layerX;
} else {
offsetX = e.pageX - target.offsetLeft;
}
var pos = offsetX / target.offsetWidth;
audio.currentTime = pos * audio.duration;
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
}
$scope.addEvents = function() {
//time update
// progress update
audio.addEventListener('timeupdate', function() {
$scope.currentTime = calCulateTime();
$scope.progressValue = calCulateProgress();
$scope.$apply();
}, false);
audio.addEventListener('ended', function() {
//auto play next
if ($scope.audioCollection && $scope.audioCollection.length > 0) {
$scope.playForward();
}
});
//angular seems dont support input[range] stuff so let's do it event
volume_range.addEventListener('change', function() {
audio.volume = parseFloat(this.value / 10);
}, false);
}
}],
link: function(scope, ele, attrs) {
scope.init();
}
}
}
])
})();

How to count the number of elements selected in a directive?

If you have a career-box directive like this:
<div ng-repeat='career in careers' career-box ng-click="toggleSelected()"> {{ career.name }} </div>
The directive:
app.directive('careerBox', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.selected = false,
scope.toggleSelected = function() {
element.toggleClass('selected');
if(scope.selected) {
scope.selected = false;
} else {
scope.selected = true;
}
}
}
};
});
Every time a career-box is selected I would like to count the total number of career-boxes selected (selected=true) and enable a button if they are more than 0.
How can I do that in the code?
There is no need to write a custom directive. It can be done with Angular core directives:
vm.selectedCount = function(item) {
var count=0;
for (let i=0; i<vm.careers.length; i++) {
if (vm.careers[i].selected) count++;
}
return count;
}
<div ng-repeat='career in careers'
ng-click="career.selected = !career.selected"
ng-style="{'background-color': career.selected?'yellow':''}">
{{ career.name }}
</div>
<hr>Selected count: {{selectedCount()}}
The DEMO on PLNKR
The DEMO
angular.module("myApp",[]);
angular.module("myApp").controller("myVm", function($scope) {
var vm = $scope;
vm.careers = [ {name: "Police Officer", value: 1},
{name: "Nurse", value:2},
{name: "Doctor", value:3},
];
vm.selectedCount = function(item) {
var count=0;
for (let i=0; i<vm.careers.length; i++) {
if (vm.careers[i].selected) count++;
}
return count;
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="myVm">
<h1>Selected Div Example</h1>
<div ng-repeat='career in careers'
ng-click="career.selected = !career.selected"
ng-style="{'background-color': career.selected?'yellow':''}">
{{ career.name }}
</div>
<hr>Selected count: {{selectedCount()}}
</body>
First, you need to have a select element with the multiple property:
<select name="carers" multiple id="multipleCarers" ng-model="data.multipleCarers">
<option ng-repeat="career in careers" value="{{career.value}}">
{{career.name}}
</option>
</select>
After of that, you can get the count of options selected:
{{data.multipleCarers.length}}

Angularjs Price Range Slider dynamically manipulate content based on Min-Max

I am new to AngularJS & I am using angular-slider for specifying a price range. I want to display the only elements specified by the range.
Here's the AngularJS Code-
var app = angular.module('myApp', ['rzModule']);
app.controller('SampleController', function($scope, $http) {
$scope.elements = [1530,2100,2780,3323,3420,4680,5020,5300,5402];
});
app.controller('RangeController', RangeController);
function RangeController() {
var vm = this;
vm.changeListener = function() {
minPrice = vm.slider.minValue;
maxPrice = vm.slider.maxValue;
console.log(minPrice + " " +maxPrice);
};
vm.slider = {
minValue: 1500,
maxValue: 5500,
options: {
floor: 1500,
ceil: 5500,
step: 500,
translate: function(value) {
return '₹' + value;
},
onChange:vm.changeListener
}
}
}
HTML-
<body ng-app="myApp">
<div ng-controller="RangeController as vm">
<rzslider rz-slider-model="vm.slider.minValue" rz-slider-high="vm.slider.maxValue" rz-slider-options="vm.slider.options"></rzslider>
</div>
<div ng-controller="SampleController">
<div ng-repeat="x in elements">
{{x}}
</div>
</div>
I think it can be done using filters but I am not sure. Any Solutions?
You can write a Custom AngularJS filter for this.
angular.module('myModule', [])
.filter('inRange', function() {
return function(array, min, max) {
array = array.filter(function(element) {
return element >= min && element <= max;
});
};
});
});
<div ng-repeat="x in elements | inRange:vm.slider.minValue:vm.slider.maxValue">

Drawing morris chart in angular directive almost shows up

I'm trying to draw a morris chart in an angular directive that is within an ng-repeat block. It is weird, because it draws, almost? I can see it's there and the mouseovers work, but the graph itself is only a thin line at the top. Does anybody have any ideas?
Here's the html:
<div id="page-wrapper" ng-repeat="d in dealerGroup.Dealerships__r" ng-if="expandedDealer == d.Id">
<div class="panel-heading">Area Chart Example</div>
<div class="panel-body">
<area-chart dealership="d" chartData="d.SalesChartData"></area-chart>
</div>
</div>
And here's the directive
angular.module('areaChart', ['ui.bootstrap']).directive('areaChart', function($window) {
var directive = {};
// directive.templateUrl = directivePath + '/charts/area-chart.html';
directive.restrict = 'EA';
directive.scope = {
dealership: "=",
chartdata: "="
};
directive.controller = function($scope) {
$scope.ykeys = function() {
var ykeys = [];
angular.forEach($scope.chartdata, function(d,k) {
angular.forEach(d, function(value,key) {
if(key != 'period') { ykeys.push(key); }
})
});
return ykeys;
}
}
directive.link = function($scope,element,attrs) {
Morris.Area({
element: element,
xkey: 'period',
ykeys: $scope.ykeys(),
labels: $scope.ykeys(),
hideHover: 'auto',
pointSize: 2,
data: $scope.chartdata
});
}
return directive;
});
And here's what happens:
Additionally, resizing makes the whole thing blow up with javascript errors everywhere. But i'll worry that separately

Working example of angular-google-maps search function

Does anyone have an example of a working search box like the one the angular-google-maps-team is showing under 'search-box' on this site: https://angular-ui.github.io/angular-google-maps/#!/api
If you write something it sure does find it in a dropdown, but when you press enter, the map doesn't respond. - How can you make the map move to the correct location when you hit enter?
html:
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="true">
<ui-gmap-search-box template="searchbox.template" events="searchbox.events" position="BOTTOM_RIGHT"></ui-gmap-search-box>
<ui-gmap-marker coords="marker.coords" options="marker.options" events="marker.events" idkey="marker.id">
</ui-gmap-google-map>
js controller:
$scope.map = {
"center": {
"latitude": 52.47491894326404,
"longitude": -1.8684210293371217
},
"zoom": 15
}; //TODO: set location based on users current gps location
$scope.marker = {
id: 0,
coords: {
latitude: 52.47491894326404,
longitude: -1.8684210293371217
},
options: { draggable: true },
events: {
dragend: function (marker, eventName, args) {
$scope.marker.options = {
draggable: true,
labelContent: "lat: " + $scope.marker.coords.latitude + ' ' + 'lon: ' + $scope.marker.coords.longitude,
labelAnchor: "100 0",
labelClass: "marker-labels"
};
}
}
};
var events = {
places_changed: function (searchBox) {
var place = searchBox.getPlaces();
if (!place || place == 'undefined' || place.length == 0) {
console.log('no place data :(');
return;
}
$scope.map = {
"center": {
"latitude": place[0].geometry.location.lat(),
"longitude": place[0].geometry.location.lng()
},
"zoom": 18
};
$scope.marker = {
id: 0,
coords: {
latitude: place[0].geometry.location.lat(),
longitude: place[0].geometry.location.lng()
}
};
}
};
$scope.searchbox = { template: 'searchbox.tpl.html', events: events };
I suggest you look at examples sent to angular-google-maps github.
There's a piece of JavaScript lacking in 123Tax response which is found at https://github.com/angular-ui/angular-google-maps/blob/master/example/assets/scripts/controllers/search-box.js
And this snippet is loaded in https://github.com/angular-ui/angular-google-maps/blob/master/example/search-box.html
// the following controls the map in your Controller
$scope.map = { control: {}, center: { latitude: 37.70, longitude: -122.344 }, zoom: 9, refresh: {}};
function placeToMarker(searchBox, id) {
var place = searchBox.getPlaces();
if (!place || place == 'undefined' || place.length == 0) {
return;
}
var marker = {
id: id,
place_id: place[0].place_id,
name: place[0].name,
address: place[0].formatted_address,
latitude: place[0].geometry.location.lat(),
longitude: place[0].geometry.location.lng(),
latlng: place[0].geometry.location.lat() + ',' + place[0].geometry.location.lng()
};
// push your markers into the $scope.map.markers array
if (!$scope.map.markers) {
$scope.map.markers = [];
}
// THIS IS THE KEY TO RECENTER/REFRESH THE MAP, to your question
$scope.map.control.refresh({latitude: marker.latitude, longitude: marker.longitude});
// the following defines the SearchBox on your Controller; events call placeToMarker function above
var searchBoxEvents = {
places_changed: function (searchBox) {
placeToMarker(searchBox, id);
}
};
// this is defined on the Controller, as well. This specifies which template searchBoxEvents should match to; note the parentdiv
$scope.searchBox = { template:'searchBox.template.html', events:searchBoxEvents, parentdiv: 'searchBoxParent'};
// in your HTML, declare where you want the searchBox. parentdiv: 'searchBoxParent' above looks for the id="searchBoxParent" in HTML
<div class="col-xs-12 col-md-12" id="searchBoxParent">
<script type="text/ng-template" id="searchBox.template.html">
<input type="text" ng-model="address" placeholder="Search Address" required />
</script>
</div>
//Lastly, in HTML, make sure you wrap ui-gmap-search-box & ui-gmap-markers in ui-gmap-google-map
<ui-gmap-google-map id="map-canvas" center="map.center" zoom="map.zoom" draggable="true" options="options" control="map.control">
<ui-gmap-search-box template="searchBox.template" events="searchBox.events" parentdiv="searchBox.parentdiv"></ui-gmap-search-box>
<ui-gmap-markers idkey="map.idkey" models="map.markers" coords="'self'" icon="'icon'" click="'onClicked'" fit="true"></ui-gmap-markers>
</ui-gmap-google-map>
Gavin's answer is correct,just some more details about the 'searchbox.tpl.html of his example.
It has to be placed outside of the directive like this:
<body>
<div id="searchBoxParent"></div>
<div id="map_canvas" ng-controller="mainCtrl">
<script type="text/ng-template" id="searchbox.tpl.html">
<input type="text" placeholder="Search Box">
</script>
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="true" options="options">
<ui-gmap-search-box template="searchbox.template" events="searchbox.events" parentdiv="searchbox.parentdiv"></ui-gmap-search-box>
</ui-gmap-google-map>
</div>
<!--example-->
</body>
Working plunkr: http://embed.plnkr.co/1rpXQhcZqwJ7rv0tyK9P/ (for some reason the plunkr only worked in chrome for me but not in firefox)
I could not comment on Gavin's answer because of lack of repution, this is why I add the info as additional answer.

Resources