Sign up for your FREE personalized newsletter featuring insights, trends, and news for America's Active Baby Boomers

Newsletter
New

Portfolio Gallary With Angular 20, Tailwind Css 4, And Material Design

Card image cap

In this chapter We'll convert our single static portfolio from the previous chapter to dynamic configurable one and create a gallary of portfolio. We will continue using Angular 20, Angular Material 20, and Tailwind CSS 4. Whether you’re a beginner or an experienced developer, this chapter sets the stage for mastering Angular 20 by building a real world project.

What are we building

Karmakanban is a sophisticated web application that combines professional portfolio creation with Kanban style task organization. By the end of this course, you'll have built a full-stack application that demonstrates modern Angular development practices, responsive design, and scalable architecture. In this chapter we built two core components.

Landing Page: A vibrant, user friendly introduction to the app.

Portfolio Gallery: A dynamic grid to display public user portfolios.

These components leverage Angular 20’s latest features, Angular Material’s polished UI, and Tailwind CSS’s utility first styling to create a modern, responsive user experience.

You can visit the current version running live on karmakanban.com

Application Architecture

Last time we architectured our component for the portfolio, this time let's do some planning for the project as a whole. We'll follow a feature based architecture that is designed to evolve alongside our requirements.

src/ 
├── app/ 
│   ├── features/              # Feature modules 
│   │   ├── landing/           # All Landing page related component 
│   │   ├── profile/           # Portfolio-related components 
│   │   │   ├── pages/ 
│   │   │   │   ├── portfolio/         # Individual portfolio view 
│   │   │   │   ├── portfolio-builder/ # Portfolio creation form (WIP) 
│   │   │   │   └── profile-list/      # Portfolio gallery 
│   │   │   └── components/            # Reusable profile components 
│   │   └── dynamic-portfolio/ # Dynamic portfolio features (WIP) 
│   ├── services/              # Application services 
│   ├── types/                 # TypeScript type definitions 
│   └── app.routes.ts          # Application routing 
├── styles.scss               # Global styles and Material theme 
└── main.ts                   # Application entry point 

Here's why this matters:

  • Scalability: Easy to add new features without affecting existing code. New features (e.g., a blog section) will get their own space with self contained components, services, and tests.
  • Maintainability: Clear separation of concerns with clear contracts using types and interfaces
  • Reusability: Shared components and services
  • Performance: Lazy loading and optimized bundles resulting in Faster Time to Interactive (TTI)

Routing and Navigation:

Angular routing enables navigation between different components based on URL changes. It transforms your application into a Single Page Application where:

  • URL changes don't trigger full page reloads
  • Components are dynamically loaded based on routes
  • Browser history is maintained
  • Route parameters can be passed between components

Core Concepts and Components

1. Routes Array inside app/app.routes.ts

The heart of Angular routing which defines the mapping between URLs and components. It's an array or route objects where each route object contains:

  • path: The URL segment that matches this route
  • component: The component to load (for eager loading)
  • loadComponent: The component to load (for lazy loading)
  • children: Nested routes
  • redirectTo: Redirect to another route
  • pathMatch: How to match the path

2. Router Configuration in the app/app.config.ts

Angular uses the provideRouter() function to configure routing in standalone applications. You pass an array of Routes objects to provideRouter() to define the application's routing table.

3. Router Navigation Methods

Angular provides multiple ways to navigate programmatically and we have used couple of them in

router.navigate() used in profile-list.ts

  • Purpose: Navigate to a route programmatically
  • Parameters: Array of route segments

Let us look into some examples to understand various ways of using it

// Navigate to home 
this.router.navigate(['/']); 
 
// Navigate with parameters 
this.router.navigate(['/portfolio', 'tonystark']); 
 
// Navigate with query parameters 
this.router.navigate(['/portfolio'], {  
  queryParams: { filter: 'public' }  
}); 

router.navigateByUrl()

  • Purpose: Navigate using a complete URL string
  • Use Case: When you have a full URL path
this.router.navigateByUrl('/portfolio/tonystark'); 

Template Based Navigation with routerLink: While I prefer using programmatic navigation, But you can also use template based navigation
Basic routerLink Usage

<a routerLink="/portfolio">View Portfolios</a> 
<a routerLink="/portfolio-builder">Create Portfolio</a> 

routerLink with Parameters

<a [routerLink]="['/portfolio', username]">View Portfolio</a> 

routerLink with Query Parameters

<a [routerLink]="['/portfolio']" [queryParams]="{filter: 'public'}"> 
  Public Portfolios 
</a> 

4. Accessing Route Parameters

Path Parameters (:username)

// Route definition 
{ path: 'portfolio/:username', component: PortfolioComponent } 
 
// Access in component 
this.route.paramMap.subscribe(params => { 
  const username = params['username']; 
}); 

In our portfolio component, we access the username parameter with this path parameter.

Query Parameters (?filter=QA)
we can add the feature to filter with role or certain other value we can use this.

// Navigation 
this.router.navigate(['/portfolio'], {  
  queryParams: { filter: 'public', sort: 'name' }  
}); 
 
// Access in component 
this.route.queryParams.subscribe(params => { 
  const filter = params['filter']; 
  const sort = params['sort']; 
}); 

5. Lazy Loading with loadComponent

We have used Angular's modern lazy loading approach with loadComponent, let's see how loadComponent Works:

loadComponent: () => import('./path/to/component').then(m => m.ComponentName) 

Code Splitting: Webpack automatically creates separate chunks improving overall performance
Reduced Initial Bundle Size: Components are loaded only when needed
Caching: Loaded components are cached for subsequent visits

Services and Dependency Injection

Angular Services are again one of the fundamental building blocks in any Angular applications. They provide a way to share data, logic, and functionality across multiple components. They follow the dependency injection pattern and help maintain clean, modular, and testable code architecture.They serve as a central place to store business logic, data access, and other functionality that can be shared across multiple components. Services help in:

  • Separation of Concerns: Keeping components focused on presentation logic
  • Code Reusability: Sharing functionality across multiple components
  • Data Management: Centralizing data operations and state management
ng generate service portfolio --skip-tests 

Let's examine how we've implemented a comprehensive service in our KarmaKanban project in portfolio.service.ts

The @Injectable Decorator

The @Injectable() decorator in Angular marks a class as available to be provided and injected as a dependency within other Angular components, services, or modules. It plays a crucial role in Angular's dependency injection (DI) system. Angular's compiler uses decorators like @Injectable() to generate metadata about classes. This metadata informs Angular's injector about how to create and provide instances of the decorated class when they are requested as dependencies.

@Injectable({ 
  providedIn: 'root' 
}) 
export class MyService { 
  // Service implementation 
} 
  • providedIn: 'root': Makes the service a singleton available throughout the application
  • providedIn: 'any': Creates a new instance for each lazy-loaded module
  • providedIn: 'platform': Makes the service available across multiple applications
  • Module-level providers: Can be provided at the module level for more control

HttpClient: Making HTTP Requests

In Angular, HttpClient is a built-in service class within the @angular/common/http package, designed for making HTTP requests to backend services. It simplifies communication with APIs, enabling Angular applications to interact with RESTful services, send and receive data, and handle responses effectively.

Basic HttpClient Usage:

HttpClient leverages RxJS Observables for handling HTTP requests and responses. This means that methods like get(), post(), put(), and delete() return Observables, which need to be subscribed to in order to trigger the actual HTTP request and receive the response.
Let see how we have used them in profile-list.ts and portfolio.ts

  • Our PortfolioService demonstrates best practices with proper typing and error handling
  • We use TypeScript interfaces to ensure type safety
  • While our current implementation uses a local JSON file in the subsequent chapters when we build our backend we will extend this to use the real backend API by just changing these service functions.

Congratulations! With this we've successfully built a modern, professional portfolio website along with a portfolio gallary using Angular 20, Material Design, and Tailwind CSS.
You can find the complete code of the chapter here
KarmaKanban Portfolio Gallary

In the next part we would be developing a portfolio builder to accomodate our portfolio inside the portfolio builder.