+
+
{{ artist.name }} wrote {{ artist.albums.length }} albums:
+
+
+
+
+ Album titled {{ album.title }} by
+ {{ album.artist }} contains
+ {{ album.ntracks }} tracks
+
+
+
+
+
diff --git a/finish/src/main/frontend/src/app/app.component.spec.ts b/finish/src/main/frontend/src/app/app.component.spec.ts
new file mode 100644
index 0000000..b896306
--- /dev/null
+++ b/finish/src/main/frontend/src/app/app.component.spec.ts
@@ -0,0 +1,31 @@
+import { TestBed, async } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'frontend'`, () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app.title).toEqual('frontend');
+ });
+
+ it('should render title in a h1 tag', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.debugElement.nativeElement;
+ expect(compiled.querySelector('h1').textContent).toContain('Welcome to frontend!');
+ });
+});
diff --git a/finish/src/main/frontend/src/app/app.component.ts b/finish/src/main/frontend/src/app/app.component.ts
new file mode 100644
index 0000000..1ad9e89
--- /dev/null
+++ b/finish/src/main/frontend/src/app/app.component.ts
@@ -0,0 +1,79 @@
+// tag::importOnInitAndAngularCorePackage[]
+import { Component, OnInit } from '@angular/core';
+// end::importOnInitAndAngularCorePackage[]
+// tag::importHttpClient[]
+import { HttpClient } from '@angular/common/http';
+// end::importHttpClient[]
+// tag::importInjectable[]
+import { Injectable } from '@angular/core';
+// end::importInjectable[]
+
+// tag::atInjectable[]
+@Injectable()
+// end::atInjectable[]
+// tag::artistsServiceClass[]
+export class ArtistsService {
+ // tag::httpClientInstance[]
+ constructor(private http: HttpClient) { }
+ // end::httpClientInstance[]
+
+ // tag::artistsUrl[]
+ private static ARTISTS_URL = '/artists';
+ // end::artistsUrl[]
+
+ // tag::fetchArtistsMethod[]
+ // tag::asyncFeature[]
+ async fetchArtists() {
+ // end::asyncFeature[]
+ try {
+ // tag::httpInstanceAndAwaitFeatureAndHttpGetAndToPromise[]
+ const data: any = await this.http.get(ArtistsService.ARTISTS_URL).toPromise();
+ // end::httpInstanceAndAwaitFeatureAndHttpGetAndToPromise[]
+ return data;
+ } catch (error) {
+ console.error(`Error occurred: ${error}`);
+ }
+ }
+ // end::fetchArtistsMethod[]
+}
+// end::artistsServiceClass[]
+
+// tag::atComponent[]
+@Component({
+ selector: 'app-root',
+ // tag::templateUrl[]
+ templateUrl: './app.component.html',
+ // end::templateUrl[]
+ // tag::providersProperty[]
+ providers: [ ArtistsService ],
+ // end::providersProperty[]
+ // tag::styleUrls[]
+ styleUrls: ['./app.component.css']
+ // end::styleUrls[]
+})
+// end::atComponent[]
+// tag::appComponentClass[]
+// tag::onInitInterface[]
+export class AppComponent implements OnInit {
+// end::onInitInterface[]
+ // tag::artistsClassMember[]
+ artists: any[] = [];
+ // end::artistsClassMember[]
+
+ // tag::artistsServiceInstanceDeclaration[]
+ constructor(private artistsService: ArtistsService) { }
+ // end::artistsServiceInstanceDeclaration[]
+
+ // tag::ngOnInitMethod[]
+ ngOnInit() {
+ // tag::thenClause[]
+ // tag::artistsServiceInstance[]
+ this.artistsService.fetchArtists().then(data => {
+ // end::artistsServiceInstance[]
+ this.artists = data;
+ });
+ // end::thenClause[]
+ }
+ // end::ngOnInitMethod[]
+}
+// end::appComponentClass[]
diff --git a/finish/src/main/frontend/src/app/app.module.ts b/finish/src/main/frontend/src/app/app.module.ts
new file mode 100644
index 0000000..dd6f5c9
--- /dev/null
+++ b/finish/src/main/frontend/src/app/app.module.ts
@@ -0,0 +1,25 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+// tag::importHttpClientModule[]
+import { HttpClientModule } from '@angular/common/http';
+// end::importHttpClientModule[]
+import { AppComponent } from './app.component';
+
+// tag::atNgModule[]
+@NgModule({
+ declarations: [
+ AppComponent
+ ],
+ // tag::importsArray[]
+ imports: [
+ BrowserModule,
+ // tag::httpClientModule[]
+ HttpClientModule,
+ // end::httpClientModule[]
+ ],
+ // end::importsArray[]
+ providers: [],
+ bootstrap: [AppComponent]
+})
+// end::atNgModule[]
+export class AppModule { }
diff --git a/finish/src/main/frontend/src/assets/.gitkeep b/finish/src/main/frontend/src/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/finish/src/main/frontend/src/browserslist b/finish/src/main/frontend/src/browserslist
new file mode 100644
index 0000000..37371cb
--- /dev/null
+++ b/finish/src/main/frontend/src/browserslist
@@ -0,0 +1,11 @@
+# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+#
+# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11
\ No newline at end of file
diff --git a/finish/src/main/frontend/src/environments/environment.prod.ts b/finish/src/main/frontend/src/environments/environment.prod.ts
new file mode 100644
index 0000000..3612073
--- /dev/null
+++ b/finish/src/main/frontend/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/finish/src/main/frontend/src/environments/environment.ts b/finish/src/main/frontend/src/environments/environment.ts
new file mode 100644
index 0000000..7b4f817
--- /dev/null
+++ b/finish/src/main/frontend/src/environments/environment.ts
@@ -0,0 +1,16 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/finish/src/main/frontend/src/favicon.ico b/finish/src/main/frontend/src/favicon.ico
new file mode 100644
index 0000000..8081c7c
Binary files /dev/null and b/finish/src/main/frontend/src/favicon.ico differ
diff --git a/finish/src/main/frontend/src/index.html b/finish/src/main/frontend/src/index.html
new file mode 100644
index 0000000..3faefb6
--- /dev/null
+++ b/finish/src/main/frontend/src/index.html
@@ -0,0 +1,14 @@
+
+
+
+