Today, we will build a personal profile page that can load and select a few different profiles.

We will start by creating a project:

ng new profile-app

Data sources

We need a way to describe people. Let’s create a new file, src/app/profile.ts and describe a Profile class.

src/app/profile.ts
export class Profile {
	name: string;
	age: number;
	contact: Contact;
}

export class Contact {
	phone: string;
	address: string;
	email: string;
}

And now we need some sample data. Here is an example:

src/app/profiles.ts
import { Profile } from './profile';

export const PROFILES: Profile[] = [
	{ name: 'Chris', age: 21, contact: { phone: '502-867-5309', address: '', email: ''}},
	{ name: 'Jerry', age: 44, contact: { phone: '309-867-5309', address: '', email: ''}},
];

Displaying a menu

Gererate a new component:

ng generate component profile-container

We should now have a new set of files in src/app/profile-container

Let’s replace the contents of src/app/app.component.html with the <app-profile-container> tag to put it on our page.

src/app/app.component.html
<h1>{{title}}</h1>

<app-profile-container></app-profile-container>

We need to import our profile data and make it available to the view. We will do that in the component TypeScript file:

src/app/profile-container/profile-container.component.ts
import { Component, OnInit } from '@angular/core';
import { Profile } from '../profile';
import { PROFILES } from '../profiles';

@Component({
  selector: 'app-profile-container',
  templateUrl: './profile-container.component.html',
  styleUrls: ['./profile-container.component.css']
})
export class ProfileContainerComponent implements OnInit {
  selectedProfile: Profile;
  profiles = PROFILES;

  onSelect(profile: Profile) {
	  this.selectedProfile = profile;
  }

  constructor() { }

  ngOnInit() {
  }

}

Notice, we have a public property, selectedProfile that will be used to store the "active" profile being displayed. It is empty by default meaning that we have not selected a profile to view yet.

We also have profiles which is where we will pull information for the profile list. The onSelect function selects exactly which profile is being displayed by updating the selectedProfile property.

Now we need to create a list of users in the component.

src/app/profile-container/profile-container.component.html
Profiles:

<ul>
	<li *ngFor="let profile of profiles" (click)="onSelect(profile)">{{profile.name}}</li>
</ul>

<div *ngIf="selectedProfile">
	<app-profile [profile]="selectedProfile"></app-profile>
</div>

<div *ngIf="!selectedProfile">
	No profile selected. Please click a profile.
</div>

Things to notice:

  • *ngFor lets us repeat items in a list

  • (click) lets us execute a method based on the element being clicked

  • We are showing a profile if one has been selected by using *ngIf

    • Otherwise, we show a default message

Profile display

As noticed in the previous template, we need a new component to display the actual profile information. It gets that data passed in using the [profile] property.

As before, create a new component:

ng generate component profile

This will create the src/app/profile directory with a standard set up template items.

Let’s first set up the component.ts file to accept that [profile] input property.

We must import the Input class from @angular/core to accept input properties. With that, we will create a new property on the class called profile:

src/app/profile/profile.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Profile } from '../profile';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  @Input() profile: Profile;

  constructor() { }

  ngOnInit() {
  }

}

And now we can create the HTML template to display that profile information:

src/app/profile/profile.component.html
<app-profile-photo></app-profile-photo>

<div class="profileInfo">
  <div class="about"><span class="name">{{profile.name}}</span> - {{profile.age}}</div>

  <div class="contact">Call: {{profile.contact.phone}}</div>
</div>

You might notice there is a component referenced in this file for a photo. How might that be filled in? This is an exercise for you.

Styling

This page is currently very ugly. We should upgrade its appearance with ng-bootstrap.

Install it with:

ng add @ng-bootstrap/ng-bootstrap

We have now added the Bootstrap CSS to our angular.json file so that the Bootstrap styles take effect. My file has a "styles" key around line 25:

angular.json SNIPPET
{
	"styles": [
		"src/styles.css",
		"node_modules/bootstrap/dist/css/bootstrap.min.css"
	]
}

If you are currently running the webserver, you will need to restart it in order for this change to take effect.

We have also must updated app.module.ts to enable support for bootstrap and models. We import the FormsModule and the NgbModule and add them to the imports key in @NgModule:

src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { ProfileComponent } from './profile/profile.component';
import { ProfilePhotoComponent } from './profile-photo/profile-photo.component';
import { ProfileContainerComponent } from './profile-container/profile-container.component';

@NgModule({
  declarations: [
    AppComponent,
    ProfileComponent,
    ProfilePhotoComponent,
    ProfileContainerComponent
  ],
  imports: [
    BrowserModule,
	FormsModule,
	NgbModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

There are many things we can do with Bootstrap, but for now we will concentrate on updating the clickable names to be buttons.

We will use Radio Buttons to update this UI. You can copy their example and modify it. Here’s my modification in the profile-container template:

<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" [(ngModel)]="model">
  <label ngbButtonLabel class="btn-primary" *ngFor="let profile of profiles">
    <input ngbButton type="radio" [value]="profile.name"> {{profile.name}}
  </label>
</div>

We also have to update the profile-container.component.ts file in order to support the [(ngModel)] property, which remembers which button is pressed. We must add a model property, which I just initialized to an empty string so that no radio is selected by default.