In this blog post, we take a look at two components from the angular/component repository: Angular Google Maps component and Angular YouTube Player component.
In the past, the angular/component repository was named angular/material and hosted only the Material Design components. Over time other components were added to the repository like the Component Dev Kit (CDK), the YouTube Player and Google Maps component. The Angular developers decided in 2019 to rename the repository to angular/component to emphasize the fact that this repository is not just the home for the Material Design components but also other components.
The two components we take a look in this blog post were wrappers around existing JavaScript libraries: Google Maps API and YouTube IFrame Player API.
Demo program ¶
The demo application I wrote for this blog post is a trivial example that displays a Google map with several hardcoded markers. A click on the markers opens an info window and plays a YouTube video from this location.
You find the complete source code on GitHub:
https://github.com/ralscha/blog2020/tree/master/locationvideo
The demo application is online under this address:
https://omed.hplar.ch/locationvideo/
Google Maps component ¶
This is an Angular component that wraps the Google Maps API JavaScript library.
Setup ¶
Install the component
npm install @angular/google-maps
You need an API key from Google to access Google Maps. Follow these steps to get a key:
https://developers.google.com/maps/get-started
Before you can use the Angular component, you need to load the Google Maps JavaScript API.
The Google Maps JavaScript API must be loaded before the GoogleMap Angular component.
In the demo application, I added a <script>
tag to index.html
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAZ..."></script>
Finally, import GoogleMapsModule
into your Angular application.
import {GoogleMapsModule} from '@angular/google-maps';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
GoogleMapsModule, // <------
...
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
Usage ¶
The Angular Google Maps library provides multiple components that wrap classes from the Google Maps JavaScript API.
In this blog post, I focus on the three components, google-map
, map-marker
, and map-info-window
.
google-map ¶
You can configure the map with the [options]
input property. It accepts a google.maps.MapOptions
object.
The component provides convenience inputs for setting the [center]
and [zoom]
without creating an entire google.maps.MapOptions
object.
To specify the size, pass string values to the inputs [height]
and [width]
.
The following events from the underlying google.maps.Map
class are supported:
boundsChanged: Observable<void>
centerChanged: Observable<void>
mapClick: Observable<google.maps.MouseEvent | google.maps.IconMouseEvent>
mapDblclick: Observable<google.maps.MouseEvent>
mapDrag: Observable<void>
mapDragend: Observable<void>
mapDragstart: Observable<void
headingChanged: Observable<void
idle: Observable<void>
maptypeidChanged: Observable<void>
mapMousemove: Observable<google.maps.MouseEvent>
mapMouseout: Observable<google.maps.MouseEvent>
mapMouseover: Observable<google.maps.MouseEvent>
projectionChanged: Observable<void>
mapRightclick: Observable<google.maps.MouseEvent>
tilesloaded: Observable<void>
tiltChanged: Observable<void>
zoomChanged: Observable<void>
The component provides the following methods. You can access these in your TypeScript code when you reference the component with @ViewChild
.
fitBounds(bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral, padding?: number | google.maps.Padding): void
panBy(x: number, y: number): void
panTo(latLng: google.maps.LatLng | google.maps.LatLngLiteral): void
panToBounds(latLngBounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral, padding?: number | google.maps.Padding): void
getBounds(): google.maps.LatLngBounds | null
getCenter(): google.maps.LatLng
getClickableIcons(): boolean
getHeading(): number
getMapTypeId(): google.maps.MapTypeId | string
getProjection(): google.maps.Projection | null
getStreetView(): google.maps.StreetViewPanorama
getTilt(): number
getZoom(): number
get controls(): Array<google.maps.MVCArray<Node>>
get data(): google.maps.Data
get mapTypes(): google.maps.MapTypeRegistry
get overlayMapTypes(): google.maps.MVCArray<google.maps.MapType>
map-marker ¶
The MapMarker component displays a marker on the map. It has to be a content child of a GoogleMap component. It provides the [options]
input to configure the component. This input accepts a google.maps.MarkerOptions
object.
Alternatively, you can configure the component with the convenience inputs [title]
, [position]
,
[label]
, and [clickable]
without creating an entire google.maps.MarkerOptions
object.
The component emits the following events.
animationChanged: Observable<void>
mapClick: Observable<google.maps.MouseEvent>
clickableChanged: Observable<void>
cursorChanged: Observable<void>
mapDblclick: Observable<google.maps.MouseEvent>
mapDrag: Observable<google.maps.MouseEvent>
mapDragend: Observable<google.maps.MouseEvent>
draggableChanged: Observable<void>
mapDragstart: Observable<google.maps.MouseEvent>
flatChanged: Observable<void>
iconChanged: Observable<void>
mapMousedown: Observable<google.maps.MouseEvent>
mapMouseout: Observable<google.maps.MouseEvent>
mapMouseover: Observable<google.maps.MouseEvent>
mapMouseup: Observable<google.maps.MouseEvent>
positionChanged: Observable<void>
mapRightclick: Observable<google.maps.MouseEvent>
shapeChanged: Observable<void>
titleChanged: Observable<void>
visibleChanged: Observable<void>
zindexChanged: Observable<void>
The component provides the following methods.
getAnimation(): google.maps.Animation | null
getClickable(): boolean
getCursor(): string | null
getDraggable(): boolean
getIcon(): string | google.maps.Icon | google.maps.Symbol | null
getLabel(): google.maps.MarkerLabel | null
getOpacity(): number | null
getPosition(): google.maps.LatLng | null
getShape(): google.maps.MarkerShape | null
getTitle(): string | null
getVisible(): boolean
getZIndex(): number | null
map-info-window ¶
The MapInfoWindow component displays an info window. Like the two components above, it provides an [options]
input to configure the component. This input property accepts a google.maps.InfoWindowOptions
object.
You can also set the position with the convenience [position]
input, without creating an entire google.maps.InfoWindowOptions
object.
The content of the component is used as the content for the info window. It keeps the structure and CSS styles when displayed as info. The MapInfoWindow component must be a child of a GoogleMap component, and you must call the open()
method to display the info. This method accepts a MapMarker object as optional argument. If provided, the info window appears next to the marker. If omitted, the position property of the options input is used.
The component emits the following events.
closeclick: Observable<void>
contentChanged: Observable<void>
domready: Observable<void>
positionChanged: Observable<void>
zindexChanged: Observable<void>
The component provides the following methods.
close(): void
getContent(): string | Node
getPosition(): google.maps.LatLng | null
getZIndex(): number
open(anchor?: MapMarker): void
Demo application ¶
The TypeScript code defines a hardcoded array of marker objects with the marker option, the YouTube video id, and a text that is displayed in the info window when the user clicks on the marker.
type MarkerObject = {
option: google.maps.MarkerOptions,
position: google.maps.LatLngLiteral,
videoId: string,
info: string
};
markers: MarkerObject[] = [{
option: {title: 'Paradise Beach', position: {lat: 13.6131648, lng: 98.1379005}},
videoId: '85tl3vKF6sw',
info: 'Paradise Beach, one of the most pristine beach in Myanmar'
}, {
option: {title: 'Hpa-An / Mawlamyine', position: {lat: 16.8841401, lng: 97.6160162}},
videoId: 'vnWP7rhHAJU',
info: 'Aerial view of Hpa-An and Mawlamyine.'
}, {
...
The class additionally defines the latitude and longitude of the map centre, the zoom level and the property info
that is used for displaying the information in the info window.
center = {lat: 19.689138, lng: 95.970246};
zoom = 6;
info: string | null = null;
The code in the template passes the variables from TypeScript to the Google Maps components.
It loops over the marker objects and creates map-marker
components, and it creates
one info window bound to the info
property of the class. This way we can dynamically change the
content of the info window.
<google-map [center]="center" [zoom]="zoom" height="100%" width="100%">
<map-marker #markerElement="mapMarker"
(mapClick)="openInfoWindow(markerElement, marker)"
*ngFor="let marker of markers"
[options]="marker.option"></map-marker>
<map-info-window>{{info}}</map-info-window>
</google-map>
When the user clicks on a marker, the component fires the mapClick
event,
which calls the openInfoWindow()
method. The application passes a reference to the map-marker
element
and the custom marker object to this method. The method then assigns the info
property from the custom marker object to the info
property of the class, and then programmatically opens the info window with open()
. By passing the reference to the map-marker
element, the info window appears at the position of the clicked marker.
@ViewChild(MapInfoWindow) infoWindow: MapInfoWindow;
openInfoWindow(markerElement: MapMarker, marker: MarkerObject) {
...
this.info = marker.info;
this.infoWindow.open(markerElement);
}
More information ¶
To learn more about the Google Maps component check out the project readme
https://github.com/angular/components/tree/main/src/google-maps
and the Google Maps JavaScript API documentation https://developers.google.com/maps/documentation/javascript/overview
YouTube Player component ¶
This is an Angular component that wraps the YouTube IFrame Player API JavaScript library.
Setup ¶
Install the component
npm install @angular/youtube-player
Import YouTubePlayerModule
into your Angular application.
import {YouTubePlayerModule} from '@angular/youtube-player';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
...
YouTubePlayerModule // <---------
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
Load the JavaScript API. The demo application does this by dynamically creating a <script>
tag in ngOnInit()
.
The YouTube JavaScript API does not require an API key.
ngOnInit(): void {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
document.body.appendChild(tag);
}
For more information, check out the Getting Started guide.
Usage ¶
The library provides the component <youtube-player>
you can add to your template.
To play a video, you need to assign a valid id to the videoId
input property.
<youtube-player videoId="HabG0k6Ycvw"></youtube-player>
You find this id in the address bar of your browser when you watch a YouTube video.
It's the value of the v
query parameter.
Input ¶
You may change the width and height of the player window with the input properties [height]
and [width]
. Both properties expect a number (pixels).
With the input properties [startSeconds]
and [endSeconds]
, you can specify at what moment in the video the player should start respectively end. Both properties expect a number (seconds).
With the input property [suggestedQuality]
you can specify the desired video quality. You can pass the following values to this input property:
"small"
"medium"
"large"
"hd720"
"hd1080"
"highres"
Events ¶
The youtube-player
component emits the following events.
(ready)
This event fires whenever the player has finished loading.
The event object is of type YT.PlayerEvent
and the target
property is a reference to the YT.Player
(stateChange)
This event fires whenever the player's state changes. The event object is of type YT.OnStateChangeEvent
and provides a data
property which is a number
describing the new player state:
- -1 (YT.PlayerState.UNSTARTED)
- 0 (YT.PlayerState.ENDED)
- 1 (YT.PlayerState.PLAYING)
- 2 (YT.PlayerState.PAUSED)
- 3 (YT.PlayerState.BUFFERING)
- 5 (YT.PlayerState.CUED)
When the player first loads a video, it broadcasts an unstarted (-1) event. When a video is cued and ready to play, the player broadcasts a video cued (5) event.
(error)
This event fires if an error occurs in the player. The event object is of type YT.OnErrorEvent
and provides a data
property which is a number specifying the error code:
2
: The request contained an invalid parameter value.5
: The requested content cannot be played in an HTML5 player.100
: The requested video was not found.101
and150
: The owner of the requested video does not allow it to be played in embedded players.
(playbackQualityChange)
This event fires whenever the video playback quality changes. The event object is of type YT.OnPlaybackQualityChangeEvent
and provides a data
property, which is a string identifying the new playback quality. These are the same values you can pass to the [suggestedQuality]
input property.
(playbackRateChange)
This event fires whenever the video playback rate changes. The event is of type YT.OnPlaybackRateChangeEvent
and provides a data
property
which is a number identifying the new playback rate.
Methods ¶
The component class YouTubePlayer
provides several methods, you can call from TypeScript
to programmatically control the player.
Play, pause, and stop the video:
playVideo(): void
pauseVideo(): void
stopVideo(): void
seekTo(seconds: number, allowSeekAhead: boolean): void
Volume
Playback rate
setPlaybackRate(playbackRate: number): void
getPlaybackRate(): number
getAvailablePlaybackRates(): number[]
Playback status
getCurrentTime(): number
getVideoLoadedFraction(): number
getPlayerState(): YT.PlayerState | undefined
Video information
Demo application ¶
The demo application uses the following code in the template.
<youtube-player (ready)="onReady($event)" (stateChange)="onStateChange($event)"
*ngIf="videoId"
[videoId]="videoId"></youtube-player>
The component is hidden until the user selects a marker. The component listens for the ready
and stateChange
event.
The TypeScript code references the component with @ViewChild
videoId: string | null = null;
@ViewChild(YouTubePlayer) youtubePlayer!: YouTubePlayer;
When the user clicks on a marker the application checks if there is currently a video playing and stops it.
openInfoWindow(markerElement: MapMarker, marker: MarkerObject): void {
if (this.youtubePlayer
&& this.youtubePlayer.getPlayerState() === YT.PlayerState.PLAYING) {
this.youtubePlayer.stopVideo();
}
The ready
and stateChange
event handlers are responsible for starting the video as soon as it's loaded. The first time the application sets the videoId
property, it displays the component and emits the ready
event. The video is cued at this stage, so the component does not emit the stateChange
event.
Following changes of the videoId
trigger only the stateChange
event. This event handler is called multiple times. As soon as the state is YT.PlayerState.CUED
(5) the video is ready for playback.
onReady(event: YT.PlayerEvent): void {
event.target.playVideo();
}
onStateChange(event: YT.OnStateChangeEvent): void {
if (event.data === YT.PlayerState.CUED) {
event.target.playVideo();
}
}
More information ¶
Check out the project readme and the JavaScript API documentation if you want to learn more about the YouTube Player component
Project readme:
https://github.com/angular/components/tree/main/src/youtube-player
YouTube IFrame Player API:
https://developers.google.com/youtube/iframe_api_reference