Table of contents
Introduction
If you have seen Amazon you will notice that they use cards quite a lot. Infacts cards are a common feature of many websites, and it makes sense if we make it a component thus increasing reusability.
In this tutorial we will try to replicate the same using Angular, and in the process will cover a few topics
Concepts Covered -
- Creating components
- Interpolation and property binding
- ngIf & ngFor directives
- Passing data from parent to child component
Before you get overwhelmed, lets get a gist of these concepts in following section
Concepts that will be used
Interpolation
Text interpolation lets you incorporate dynamic string values into your HTML templates i.e. Get data from class to template Example -
// In app.component.ts
title = 'Hello World';
// In app.component.html
<h1>{{title}}</h1> // insert between html tags
<h1 innerText={{title}}> // Or in html attribute
Property binding
Property binding in Angular helps you set values for properties of HTML elements or directives. We can use property binding to implement toggle button functionality, set paths programmatically, and share values between components. Most of the time interpolation and property binding can achieve same thing.
<img [src]='product.imageUrl'> // Write template expn inside quotes
<img src={{'home/products/'+product.imageUrl}}> Interpolation is usefull in longer expns
ngIf & ngFor directives
Directives are classes that add additional behavior to elements in your Angular applications. There are 3 type of directives - Components, Attribute directives (NgClass,NgStyle) , Structural directives (NgFor, NgSwitch)
//ngIf example
<div *ngIf='products.length'; else ElsePart>Display if part</div>
<ng-template #ElsePart>Something else</ng-template>
//ngfor example
<ul>
<li *ngFor="let product of products">{{product.name}}</li>
</ul>
Passing data from parent to child
Using @Input decorator in a child component or directive we can signify that the property can receive its value from its parent component.
// Inside parent.component.html
<child [data]='someData'></child>
// Inside child.component.ts
export class ChildComponent{
@Input() data: string;
}
Setting up project
First of lets create a project, we will be using Angular CLI( a command line tool) for helping us create a project
// Create a new project
ng new cards
// Create a component card
ng g c card
We create a card component, for the purpose of reusability, now everytime we need card we can use in app or any other component. Our project structure should look something like this -
Note on Dynamic data
Now supposedly we will get data from some api/file in parent(app) component and we will have to pass it to child(card) component. For simplicity of this tutorial, let's say we have receive data in following format and need to pass this to card component.
posts = [
{
title: 'card title ',
links: ['#','#',..],
imageUrls: ['url1','url2',..],
imageCaptions: ['caption1','caption2',..]
}, .. ]
Working on Application
Now we have enough conceptual knowledge to create cards.
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
// data we will pass from parent to child (In real world, will get from api)
posts = [
{
title: 'Amazon Pay | Pay utility bills fast & conveniently',
links: ['#','#','#','#'],
imageUrls: [
'https://images-eu.ssl-images-amazon.com/images/G/31/img19/AmazonPay/Kartik/FEBGTM/Electricity_186x116._SY116_CB646259375_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img19/AmazonPay/Kartik/FEBGTM/LPG_186x116._SY116_CB646259375_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img19/AmazonPay/Kartik/FEBGTM/Broadband_186x116._SY116_CB646259375_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img19/AmazonPay/Kartik/FEBGTM/PCrevised/DTH_186x116._SY116_CB628894454_.jpg'],
imageCaptions: ['Electricity Bill','LPG Gas cylinder','Broadband bill', 'DTH Recharge']
},
{
title: 'Amazon Pay | Book your travel tickets',
links: ['#','#','#','#'],
imageUrls: [
'https://images-eu.ssl-images-amazon.com/images/G/31/img16/AmazonPayTravels/November/GWPercolate/Flight_186x116._SY116_CB653435429_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img16/AmazonPayTravels/November/GWPercolate/Bus_186x116._SY116_CB653435429_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img16/AmazonPayTravels/November/GWPercolate/Train_186x116._SY116_CB653435429_.jpg',
'https://images-eu.ssl-images-amazon.com/images/G/31/img16/AmazonPayTravels/November/GWPercolate/essential_186x116._SY116_CB653435429_.jpg'],
imageCaptions: ['Flight Tickets', 'Bus Tickets', 'Train Tickets', 'Essenbtial Travel products']
}
]
}
app.component.html
<app-card
*ngFor="let post of posts"
[title]="post.title"
[imageUrls]="post.imageUrls"
[imageCaptions]="post.imageCaptions"
[links]="post.links"
></app-card>
card.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.css']
})
export class CardComponent implements OnInit {
// Expecting data from parent
@Input() title='';
@Input() imageUrls: string[] = [];
@Input() imageCaptions: string[] = [];
@Input() links: string[] = [];
constructor() { }
ngOnInit(): void {
}
}
card.component.html
<div class="card">
<h1>{{title}}</h1>
<div *ngFor="let img of imageUrls; let i=index;" class="card-box">
<a [href]="links[i]">
<img [src]="img" >
<p *ngIf="imageCaptions[i]; else defaultCaption">{{imageCaptions[i]}}</p>
<ng-template #defaultCaption>No caption available</ng-template>
</a>
</div>
</div>
Styling the application
app.component.css
:host {
display: flex;
justify-content: center;
}
card.component.css
.card {
max-width: 350px;
height: 420px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
border-radius: 2px;
margin: 10px;
box-shadow: 7px 3px 11px -7px rgba(0, 0, 0, 0.75);
-webkit-box-shadow: 7px 3px 11px -7px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 7px 3px 11px -7px rgba(0, 0, 0, 0.75);
}
.card h1 {
text-align: center;
font-size: 1.1rem;
font-weight: 700;
height: 30px;
padding: 5px;
}
.card-box {
width: 165px;
padding: 8px;
}
.card-box img {
width: 100%;
}
.card-box p {
font-size: 12px;
}
.card-box a {
text-decoration: none;
color: black;
}
.card-box a:hover {
text-decoration: none;
color: rgb(87, 87, 87);
filter: opacity(90%);
}