Full Trust European Hosting

BLOG about Full Trust Hosting and Its Technology - Dedicated to European Windows Hosting Customer

AngularJS Hosting Europe - HostForLIFE :: Authentication and Login (Angular)

clock January 29, 2026 08:16 by author Peter

In this lecture, we will use PrimeNg Free Template sakai-ng and Angular 20 to create a complete Backoffice Admin Panel.

This system will allow your administrators to:

  • Manage products
  • Update settings
  • Handle security & authorization

Interact with the backend APIs you've previously created

Additionally, we will develop a clear, scalable Angular architecture and use JWT authentication.

1. Angular environment setup We will Use Free Version of PrimeNG (sakai-ng)

Clone Project
git clone https://github.com/primefaces/sakai-ng

Create New environment files
Path: "/src/environments/environment.ts" and "/src/environments/environment.prod.ts"

export const environment = {
  production: false,
  // Set your API base URL here, e.g. 'https://api.example.com'
    baseUrl: 'https://localhost:7104/api'
};

2. Folder structure

3. Services + Models
Create Auth Service
Path : "/src/app/services/api/auth.service.ts"

auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { environment } from '../../../environments/environment';

export interface LoginRequest {
  email: string;
  password: string;
}

export interface RegisterRequest {
  email: string;
  password: string;
  firstName?: string;
  lastName?: string;
}

export interface RefreshRequest {
  token: string;
  refreshToken: string;
}

export interface AuthResponse {
  token: string;
  refreshToken: string;
}

@Injectable({
  providedIn: 'root'
})
export class AuthApiService {
    private baseUrl = environment.baseUrl + '/auth';



  constructor(private http: HttpClient) {}

  login(request: LoginRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/login`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  register(request: RegisterRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/register`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  refresh(request: RefreshRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/refresh`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  private saveTokens(token: string, refreshToken: string): void {
    localStorage.setItem('auth_token', token);
    localStorage.setItem('refresh_token', refreshToken);
  }

  getToken(): string | null {
    return localStorage.getItem('auth_token');
  }

  getRefreshToken(): string | null {
    return localStorage.getItem('refresh_token');
  }

  logout(): void {
    localStorage.removeItem('auth_token');
    localStorage.removeItem('refresh_token');
  }

  isAuthenticated(): boolean {
    return !!this.getToken();
  }
}


4. Authentication + Interceptor
Create Interceptor
Path: "/src/app/core/interceptors/auth.interceptor.ts"
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthApiService } from '../../app/services/api/auth.service';
import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
    const authService = inject(AuthApiService);
    const router = inject(Router);
    const token = authService.getToken();

    // Skip interceptor logic for auth endpoints (login, register, refresh)
    if (req.url.includes('/auth/login') || req.url.includes('/auth/register') || req.url.includes('/auth/refresh')) {
        return next(req).pipe(
            catchError((error) => {
                return throwError(() => error);
            })
        );
    }

    // If no token and trying to access protected routes, redirect to login
    if (!token) {
        router.navigate(['/auth/login']);
        return throwError(() => new Error('No authentication token found'));
    }

    // Clone request and add Authorization header with Bearer token
    req = req.clone({
        setHeaders: {
            Authorization: `Bearer ${token}`
        }
    });

    return next(req).pipe(
        catchError((error) => {
            // If 401 Unauthorized, clear localStorage and redirect to login
            if (error.status === 401) {
                authService.logout();
                router.navigate(['/auth/login']);
                return throwError(() => new Error('Session expired. Please login again.'));
            }
            return throwError(() => error);
        })
    );
};


5. Move Login Component Features

Path: "/src/app/features/auth/login/login.html"
"/src/app/features/auth/login/login.scss" will be Blank file
"/src/app/features/auth/login/login.ts"
"/src/app/features/auth/auth.routes.ts"


login.html
<p-toast />
<app-floating-configurator />
<div class="bg-surface-50 dark:bg-surface-950 flex items-center justify-center min-h-screen min-w-screen overflow-hidden">
    <div class="flex flex-col items-center justify-center">
        <div style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
            <div class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20" style="border-radius: 53px">
                <div class="text-center mb-8">
                    <svg viewBox="0 0 54 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="mb-8 w-16 shrink-0 mx-auto">
                        <path
                            fill-rule="evenodd"
                            clip-rule="evenodd"
                            d="M17.1637 19.2467C17.1566 19.4033 17.1529 19.561 17.1529 19.7194C17.1529 25.3503 21.7203 29.915 27.3546 29.915C32.9887 29.915 37.5561 25.3503 37.5561 19.7194C37.5561 19.5572 37.5524 19.3959 37.5449 19.2355C38.5617 19.0801 39.5759 18.9013 40.5867 18.6994L40.6926 18.6782C40.7191 19.0218 40.7326 19.369 40.7326 19.7194C40.7326 27.1036 34.743 33.0896 27.3546 33.0896C19.966 33.0896 13.9765 27.1036 13.9765 19.7194C13.9765 19.374 13.9896 19.0316 14.0154 18.6927L14.0486 18.6994C15.0837 18.9062 16.1223 19.0886 17.1637 19.2467ZM33.3284 11.4538C31.6493 10.2396 29.5855 9.52381 27.3546 9.52381C25.1195 9.52381 23.0524 10.2421 21.3717 11.4603C20.0078 11.3232 18.6475 11.1387 17.2933 10.907C19.7453 8.11308 23.3438 6.34921 27.3546 6.34921C31.36 6.34921 34.9543 8.10844 37.4061 10.896C36.0521 11.1292 34.692 11.3152 33.3284 11.4538ZM43.826 18.0518C43.881 18.6003 43.9091 19.1566 43.9091 19.7194C43.9091 28.8568 36.4973 36.2642 27.3546 36.2642C18.2117 36.2642 10.8 28.8568 10.8 19.7194C10.8 19.1615 10.8276 18.61 10.8816 18.0663L7.75383 17.4411C7.66775 18.1886 7.62354 18.9488 7.62354 19.7194C7.62354 30.6102 16.4574 39.4388 27.3546 39.4388C38.2517 39.4388 47.0855 30.6102 47.0855 19.7194C47.0855 18.9439 47.0407 18.1789 46.9536 17.4267L43.826 18.0518ZM44.2613 9.54743L40.9084 10.2176C37.9134 5.95821 32.9593 3.1746 27.3546 3.1746C21.7442 3.1746 16.7856 5.96385 13.7915 10.2305L10.4399 9.56057C13.892 3.83178 20.1756 0 27.3546 0C34.5281 0 40.8075 3.82591 44.2613 9.54743Z"
                            fill="var(--primary-color)"
                        />
                        <mask id="mask0_1413_1551" style="mask-type: alpha" maskUnits="userSpaceOnUse" x="0" y="8" width="54" height="11">
                            <path d="M27 18.3652C10.5114 19.1944 0 8.88892 0 8.88892C0 8.88892 16.5176 14.5866 27 14.5866C37.4824 14.5866 54 8.88892 54 8.88892C54 8.88892 43.4886 17.5361 27 18.3652Z" fill="var(--primary-color)" />
                        </mask>
                        <g mask="url(#mask0_1413_1551)">
                            <path
                                d="M-4.673e-05 8.88887L3.73084 -1.91434L-8.00806 17.0473L-4.673e-05 8.88887ZM27 18.3652L26.4253 6.95109L27 18.3652ZM54 8.88887L61.2673 17.7127L50.2691 -1.91434L54 8.88887ZM-4.673e-05 8.88887C-8.00806 17.0473 -8.00469 17.0505 -8.00132 17.0538C-8.00018 17.055 -7.99675 17.0583 -7.9944 17.0607C-7.98963 17.0653 -7.98474 17.0701 -7.97966 17.075C-7.96949 17.0849 -7.95863 17.0955 -7.94707 17.1066C-7.92401 17.129 -7.89809 17.1539 -7.86944 17.1812C-7.8122 17.236 -7.74377 17.3005 -7.66436 17.3743C-7.50567 17.5218 -7.30269 17.7063 -7.05645 17.9221C-6.56467 18.3532 -5.89662 18.9125 -5.06089 19.5534C-3.39603 20.83 -1.02575 22.4605 1.98012 24.0457C7.97874 27.2091 16.7723 30.3226 27.5746 29.7793L26.4253 6.95109C20.7391 7.23699 16.0326 5.61231 12.6534 3.83024C10.9703 2.94267 9.68222 2.04866 8.86091 1.41888C8.45356 1.10653 8.17155 0.867278 8.0241 0.738027C7.95072 0.673671 7.91178 0.637576 7.90841 0.634492C7.90682 0.63298 7.91419 0.639805 7.93071 0.65557C7.93897 0.663455 7.94952 0.673589 7.96235 0.686039C7.96883 0.692262 7.97582 0.699075 7.98338 0.706471C7.98719 0.710167 7.99113 0.714014 7.99526 0.718014C7.99729 0.720008 8.00047 0.723119 8.00148 0.724116C8.00466 0.727265 8.00796 0.730446 -4.673e-05 8.88887ZM27.5746 29.7793C37.6904 29.2706 45.9416 26.3684 51.6602 23.6054C54.5296 22.2191 56.8064 20.8465 58.4186 19.7784C59.2265 19.2431 59.873 18.7805 60.3494 18.4257C60.5878 18.2482 60.7841 18.0971 60.9374 17.977C61.014 17.9169 61.0799 17.8645 61.1349 17.8203C61.1624 17.7981 61.1872 17.7781 61.2093 17.7602C61.2203 17.7512 61.2307 17.7427 61.2403 17.7348C61.2452 17.7308 61.2499 17.727 61.2544 17.7233C61.2566 17.7215 61.2598 17.7188 61.261 17.7179C61.2642 17.7153 61.2673 17.7127 54 8.88887C46.7326 0.0650536 46.7357 0.0625219 46.7387 0.0600241C46.7397 0.0592345 46.7427 0.0567658 46.7446 0.0551857C46.7485 0.0520238 46.7521 0.0489887 46.7557 0.0460799C46.7628 0.0402623 46.7694 0.0349487 46.7753 0.0301318C46.7871 0.0204986 46.7966 0.0128495 46.8037 0.00712562C46.818 -0.00431848 46.8228 -0.00808311 46.8184 -0.00463784C46.8096 0.00228345 46.764 0.0378652 46.6828 0.0983779C46.5199 0.219675 46.2165 0.439161 45.7812 0.727519C44.9072 1.30663 43.5257 2.14765 41.7061 3.02677C38.0469 4.79468 32.7981 6.63058 26.4253 6.95109L27.5746 29.7793ZM54 8.88887C50.2691 -1.91433 50.27 -1.91467 50.271 -1.91498C50.2712 -1.91506 50.272 -1.91535 50.2724 -1.9155C50.2733 -1.91581 50.274 -1.91602 50.2743 -1.91616C50.2752 -1.91643 50.275 -1.91636 50.2738 -1.91595C50.2714 -1.91515 50.2652 -1.91302 50.2552 -1.9096C50.2351 -1.90276 50.1999 -1.89078 50.1503 -1.874C50.0509 -1.84043 49.8938 -1.78773 49.6844 -1.71863C49.2652 -1.58031 48.6387 -1.377 47.8481 -1.13035C46.2609 -0.635237 44.0427 0.0249875 41.5325 0.6823C36.215 2.07471 30.6736 3.15796 27 3.15796V26.0151C33.8087 26.0151 41.7672 24.2495 47.3292 22.7931C50.2586 22.026 52.825 21.2618 54.6625 20.6886C55.5842 20.4011 56.33 20.1593 56.8551 19.986C57.1178 19.8993 57.3258 19.8296 57.4735 19.7797C57.5474 19.7548 57.6062 19.7348 57.6493 19.72C57.6709 19.7127 57.6885 19.7066 57.7021 19.7019C57.7089 19.6996 57.7147 19.6976 57.7195 19.696C57.7219 19.6952 57.7241 19.6944 57.726 19.6938C57.7269 19.6934 57.7281 19.693 57.7286 19.6929C57.7298 19.6924 57.7309 19.692 54 8.88887ZM27 3.15796C23.3263 3.15796 17.7849 2.07471 12.4674 0.6823C9.95717 0.0249875 7.73904 -0.635237 6.15184 -1.13035C5.36118 -1.377 4.73467 -1.58031 4.3155 -1.71863C4.10609 -1.78773 3.94899 -1.84043 3.84961 -1.874C3.79994 -1.89078 3.76474 -1.90276 3.74471 -1.9096C3.73469 -1.91302 3.72848 -1.91515 3.72613 -1.91595C3.72496 -1.91636 3.72476 -1.91643 3.72554 -1.91616C3.72593 -1.91602 3.72657 -1.91581 3.72745 -1.9155C3.72789 -1.91535 3.72874 -1.91506 3.72896 -1.91498C3.72987 -1.91467 3.73084 -1.91433 -4.673e-05 8.88887C-3.73093 19.692 -3.72983 19.6924 -3.72868 19.6929C-3.72821 19.693 -3.72698 19.6934 -3.72603 19.6938C-3.72415 19.6944 -3.72201 19.6952 -3.71961 19.696C-3.71482 19.6976 -3.70901 19.6996 -3.7022 19.7019C-3.68858 19.7066 -3.67095 19.7127 -3.6494 19.72C-3.60629 19.7348 -3.54745 19.7548 -3.47359 19.7797C-3.32589 19.8296 -3.11788 19.8993 -2.85516 19.986C-2.33008 20.1593 -1.58425 20.4011 -0.662589 20.6886C1.17485 21.2618 3.74125 22.026 6.67073 22.7931C12.2327 24.2495 20.1913 26.0151 27 26.0151V3.15796Z"
                                fill="var(--primary-color)"
                            />
                        </g>
                    </svg>
                    <div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">Welcome to PrimeLand!</div>
                    <span class="text-muted-color font-medium">Sign in to continue</span>
                </div>

                <div>
                    <label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Email</label>
                    <input pInputText id="email1" type="text" placeholder="Email address" class="w-full md:w-120 mb-8" [(ngModel)]="email" />

                    <label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
                    <p-password id="password1" [(ngModel)]="password" placeholder="Password" [toggleMask]="true" styleClass="mb-4" [fluid]="true" [feedback]="false"></p-password>

                    <div class="flex items-center justify-between mt-2 mb-8 gap-8">
                        <div class="flex items-center">
                            <p-checkbox [(ngModel)]="checked" id="rememberme1" binary class="mr-2"></p-checkbox>
                            <label for="rememberme1">Remember me</label>
                        </div>
                        <span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">Forgot password?</span>
                    </div>
                    <p-button label="Sign In" styleClass="w-full" (onClick)="handleLogin()" [loading]="isLoading"></p-button>
                </div>
            </div>
        </div>
    </div>
</div>

login.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
import { PasswordModule } from 'primeng/password';
import { RippleModule } from 'primeng/ripple';
import { AppFloatingConfigurator } from '../../../layout/component/app.floatingconfigurator';
import { AuthApiService } from '../../../services/api/auth.service';
import { MessageService } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-login',
    standalone: true,
    imports: [
        CommonModule,
        ButtonModule,
        CheckboxModule,
        InputTextModule,
        PasswordModule,
        FormsModule,
        RouterModule,
        RippleModule,
        AppFloatingConfigurator,
        ToastModule
    ],
    providers: [MessageService],
    templateUrl: './login.html',
    styleUrl: './login.scss'
})
export class Login {
    email: string = '';
    password: string = '';
    checked: boolean = false;
    isLoading: boolean = false;

    constructor(
        private authApi: AuthApiService,
        private messageService: MessageService,
        private router: Router
    ) {}

    handleLogin() {
        if (!this.email || !this.password) {
            this.messageService.add({
                severity: 'warn',
                summary: 'Validation Error',
                detail: 'Email and password are required.',
                life: 3000
            });
            return;
        }

        this.isLoading = true;
        this.authApi.login({ email: this.email, password: this.password }).subscribe({
            next: (response: any) => {
                this.isLoading = false;
                this.messageService.add({
                    severity: 'success',
                    summary: 'Success',
                    detail: 'Login successful!',
                    life: 2000
                });
                // Redirect to dashboard after short delay
                setTimeout(() => {
                    this.router.navigate(['/dashboard']);
                }, 500);
            },
            error: (err: any) => {
                this.isLoading = false;
                const errorMsg = err.error?.errors?.[0] || 'Login failed. Please try again.';
                this.messageService.add({
                    severity: 'error',
                    summary: 'Login Failed',
                    detail: errorMsg,
                    life: 3000
                });
            }
        });
    }
}


auth.routes.ts
import { Routes } from '@angular/router';
import { Login } from './login/login';

export default [
    { path: '', redirectTo: 'login', pathMatch: 'full' },
    { path: 'login', component: Login }
] as Routes;

6. Update App Routes

Path : "src/app/app.routes.ts"
import { Routes } from '@angular/router';
import { AppLayout } from './app/layout/component/app.layout';
import { Dashboard } from './app/pages/dashboard/dashboard';
import { Documentation } from './app/pages/documentation/documentation';
import { Landing } from './app/pages/landing/landing';
import { Notfound } from './app/pages/notfound/notfound';

export const appRoutes: Routes = [
    {
        path: '',
        redirectTo: () => {
            const token = localStorage.getItem('auth_token');
            return token ? '/dashboard' : '/auth/login';
        },
        pathMatch: 'full'
    },
    {
        path: 'dashboard',
        component: AppLayout,
        children: [
            { path: '', component: Dashboard }
        ]
    },
    {
        path: 'uikit',
        component: AppLayout,
        children: [
            { path: '', loadChildren: () => import('./app/pages/uikit/uikit.routes') }
        ]
    },
    {
        path: 'pages',
        component: AppLayout,
        children: [
            { path: '', loadChildren: () => import('./app/pages/pages.routes') }
        ]
    },
    {
        path: 'documentation',
        component: AppLayout,
        children: [
            { path: '', component: Documentation }
        ]
    },
    { path: 'landing', component: Landing },
    { path: 'notfound', component: Notfound },
    { path: 'auth', loadChildren: () => import('./app/features/auth/auth.routes') },
    { path: '**', component: Notfound }
];

7. Update App Config
Path: "src/app/app.config.ts"
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router';
import Aura from '@primeuix/themes/aura';
import { providePrimeNG } from 'primeng/config';
import { appRoutes } from './app.routes';
import { authInterceptor } from '@/core/interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(appRoutes, withInMemoryScrolling({ anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled' }), withEnabledBlockingInitialNavigation()),
        provideHttpClient(withFetch(), withInterceptors([authInterceptor])),
        provideAnimationsAsync(),
        providePrimeNG({ theme: { preset: Aura, options: { darkModeSelector: '.app-dark' } } })
    ]
};

8. Lets Test
Install npm packages

npm i

After Installation Lets run both Application backend and angular

ng s -o


Click on Login

Login Sucessfully, redirected to Dashboard and toke in local storage



AngularJS Hosting Europe - HostForLIFE :: What's Replacing JWT and Why Is It Failing?

clock January 8, 2026 06:41 by author Peter

JSON Web Tokens (JWT) have been the standard for authentication in contemporary web applications for about ten years. It's likely that you have used JWT-based authentication several times if you have worked with Angular, React, or any SPA supported by REST APIs. Because it was straightforward, stateless, scalable, and simple to incorporate, it gained popularity.

However, an intriguing development has occurred in recent years. Security teams, big businesses, and senior engineers have begun to doubt JWT. Not merely minor adjustments or enhancements, but the core model itself. JWT is no longer regarded as the "best practice" approach that it formerly was. JWT is currently a source of complexity, hidden faults, and security issues in many real-world systems.

This article explains:

  • Why JWT became popular
  • Where JWT fundamentally breaks down
  • Real production issues teams face today
  • Why “just do it correctly” is not enough
  • What is replacing JWT in modern architectures
  • How Angular applications should adapt
  • Practical migration strategies

This is not a beginner-level overview. This article is written for senior developers who have built and maintained authentication systems in production.

Why JWT Became Popular in the First Place?

To understand why JWT is failing, we first need to understand why it succeeded.
The Problem JWT Originally Solved

Before JWT, authentication often relied on:

  • Server-side sessions
  • Sticky sessions
  • Shared session stores (Redis, DB)
  • Complex load balancer configurations

These approaches had real downsides:

  • Scaling sessions horizontally was painful
  • Stateful servers complicated deployments
  • Mobile and SPA apps struggled with cookies
  • JWT solved these problems elegantly.

Key Advantages of JWT
Stateless Authentication

The server does not store session data. All information is in the token.

Scales Well

Any server can validate a JWT using a shared secret or public key.

Works Across Platforms
Mobile apps, SPAs, APIs, and microservices all understand JWT.

Performance Friendly
No database or cache lookup per request.

Easy to Implement
Many libraries, tutorials, and examples.

For Angular SPAs talking to REST APIs, JWT felt perfect.

The Core Design of JWT (And Its Hidden Assumptions)
A JWT is basically:

  • A signed JSON object
  • Stored on the client
  • Sent with every request
  • Trusted by the server if the signature is valid

A typical JWT contains:

  • User ID
  • Roles or permissions
  • Expiry timestamp
  • Issuer and audience

The Hidden Assumption

JWT assumes that:
“If the token is valid and not expired, the user is authorized.”

This assumption works only in simple systems.

Modern applications are not simple.

Where JWT Starts to Fail in Real Systems?


JWT does not fail because it is “bad”.
It fails because real-world requirements evolved.

Let us look at the most common failure points.

Problem 1: Token Revocation Is Broken by Design

JWTs are immutable. Once issued, they are valid until they expire.

Why This Is a Serious Problem?

In real applications, you must be able to:

  • Log out users instantly
  • Revoke access after password change
  • Disable compromised accounts
  • Enforce policy changes

With JWT:

  1. You cannot revoke a token
  2. You can only wait for it to expire

Common “Solutions” That Don’t Really Work
Short-Lived Tokens

  • Improves security
  • Breaks UX
  • Adds refresh-token complexity

Token Blacklists

  • Reintroduces server-side state
  • Requires DB or cache lookup
  • Breaks the main benefit of JWT

At this point, JWT is no longer stateless.

Problem 2: JWT Makes Logout a Lie
In many Angular apps:

  • Clicking “Logout” just deletes the token from local storage
  • The server still considers the token valid

If the token is stolen:

  • Logout does nothing
  • Attacker keeps access until expiry

From a security perspective, this is unacceptable.

Problem 3: Token Theft Is Too Easy
JWTs are commonly stored in:

  • LocalStorage
  • SessionStorage
  • Memory

Why This Is Dangerous?
XSS attacks can steal tokens

Browser extensions can access tokens

Tokens can be leaked via logs or error reports

Once stolen:

  • The attacker has full access
  • There is no server-side kill switch

Cookies with HttpOnly flags solve some of this, but many JWT setups do not use them correctly.

Problem 4: Overloaded Tokens Become Security Liabilities

JWTs often include:

  • Roles
  • Permissions
  • Feature flags
  • Tenant data

This leads to:

  • Large token sizes
  • Stale authorization data
  • Authorization logic duplicated across services

If a user’s role changes:

  • Existing tokens still contain old roles
  • Access is incorrectly granted

This is a data consistency problem, not just a security issue.

Problem 5: Microservices + JWT = Hidden Complexity
JWT is often promoted as “microservice-friendly”.

In practice:

  1. Every service must understand token structure
  2. Authorization logic spreads everywhere
  3. Policy changes require coordinated updates

This leads to:

  • Tight coupling
  • Inconsistent access control
  • Security drift over time

Problem 6: Refresh Tokens Are a Minefield
To fix short-lived JWTs, teams introduce refresh tokens.

Now you have:

  • Access tokens
  • Refresh tokens
  • Rotation logic
  • Storage rules
  • Expiry policies

Mistakes here are common and dangerous:

  • Refresh token reuse
  • Long-lived refresh tokens
  • Poor rotation strategies

The system becomes harder to reason about than classic sessions.

Problem 7: Compliance and Auditing Are Hard
Modern regulations require:

  • Session tracking
  • Forced logout
  • Access audits
  • User-level controls

JWT makes this difficult because:

  • Sessions do not exist on the server
  • There is no single source of truth

Auditors do not like “trust the token”.

Why “Using JWT Correctly” Is Not Enough?
A common response is:
“JWT is fine, people just implement it incorrectly.”

This is partially true, but incomplete.

Even with:

  • Short-lived tokens
  • Secure cookies
  • Refresh token rotation
  • Token introspection

You end up rebuilding:

  • Sessions
  • Revocation
  • State
  • Centralized control

At that point, JWT is no longer the core solution.

So What Is Replacing JWT?
JWT is not disappearing entirely, but its role is changing.
Modern systems are moving toward controlled, state-aware authentication models.
Let us explore what is replacing JWT.

Replacement 1: Session-Based Authentication (Yes, It’s Back)

Sessions never truly went away.

What changed is:

  • Infrastructure
  • Scalability
  • Implementation quality
  • Why Sessions Are Making a Comeback

Modern session systems:

  • Use Redis or distributed caches
  • Scale horizontally
  • Support instant revocation
  • Enable strong security controls

How It Works Today?

  • Client stores a secure, HttpOnly session cookie
  • Server stores session state
  • Logout destroys the session
  • Revocation is immediate

This model:

  • Is easier to reason about
  • Is more secure by default
  • Fits compliance requirements

For Angular apps, this works very well with modern CORS and cookie policies.

Replacement 2: OAuth 2.1 with Token Introspection
OAuth 2.1 moves away from blindly trusting JWTs.
Token Introspection

Instead of validating JWT locally:

  • Services call the authorization server
  • Token validity is checked centrally

This allows:

  • Immediate revocation
  • Central policy enforcement
  • Better auditing

JWT may still exist, but it is not self-authorizing anymore.

Replacement 3: Backend-for-Frontend (BFF) Pattern

The BFF pattern is becoming the default for SPAs.

How BFF Changes Everything

  • Angular talks only to its backend
  • Backend manages authentication
  • No tokens stored in the browser
  • Cookies handle session state

Benefits:

  • No JWT in frontend
  • Reduced attack surface
  • Cleaner architecture

This is one of the strongest arguments against frontend JWT storage.

Replacement 4: Short-Lived Tokens + Server State
In some systems:

  • JWTs exist but live for seconds or minutes
  • Server tracks session state
  • Tokens are just transport credentials


JWT becomes an implementation detail, not a security boundary.

Replacement 5: Passkeys and WebAuthn (Future Direction)
Passwords themselves are being replaced.

Passkeys and WebAuthn:

  • Remove password theft
  • Reduce phishing risks
  • Integrate well with session-based auth

JWT plays a minimal role in these systems.

What This Means for Angular Applications?

Angular developers must rethink authentication.

What Angular Should NOT Do Anymore

  • Store JWT in localStorage
  • Decode tokens for authorization logic
  • Trust client-side role checks
  • Handle refresh logic in components

Recommended Angular Architecture Today

  • Use BFF pattern
  • Rely on HttpOnly cookies
  • Let backend manage auth state
  • Angular focuses on UI and UX
  • Authorization checks happen server-side

Angular becomes simpler, not more complex.

Example: Angular with Session-Based Auth

Angular makes requests like this:

this.http.get('/api/user/profile', {
withCredentials: true
});


No tokens.
No headers.
No decoding.
No expiry handling.

The backend:

  • Validates session
  • Enforces access
  • Returns data

This is boring, and that is a good thing.

Migrating Away from JWT Safely

You do not need to rewrite everything overnight.

Step-by-Step Migration Strategy

  • Introduce BFF layer
  • Move auth logic to backend
  • Switch to HttpOnly cookies
  • Reduce token lifespan
  • Gradually remove frontend JWT usage

Most teams do this incrementally.

When JWT Still Makes Sense?

JWT is not dead.

It still works well for:

  • Machine-to-machine communication
  • Internal service tokens
  • Short-lived infrastructure credentials

The key is:
Do not use JWT as a user session mechanism.


Common Misconceptions

  1. “Sessions do not scale” – they do now.
  2. “JWT is more secure” – often false.
  3. “Cookies are legacy” – incorrect.
  4. “Stateless is always better” – context matters.

Final Thoughts
JWT solved a real problem at a specific time.
But software architecture evolves.

Today’s applications need:

  • Revocation
  • Control
  • Auditing
  • Security by default

JWT, as commonly used for user authentication, fails these requirements.

What replaces it is not one thing, but a shift in mindset:

  1. From stateless trust
  2. To controlled, observable sessions

Angular developers who adapt early will build systems that are:

  • Safer
  • Simpler
  • Easier to maintain

JWT is not evil.
It is just no longer the right default.



AngularJS Hosting Europe - HostForLIFE :: Scalable Web Applications with Micro Frontends

clock December 16, 2025 07:05 by author Peter

An in-depth look at Angular, practical patterns, and production best practices. The size, complexity, and team distribution of contemporary online applications have all increased. Ten years ago, the majority of frontend teams used a single codebase and deployment workflow to create single applications. When there were fewer features, a smaller team, and simpler business requirements, that strategy proved effective. However, today's companies manage many separate release cycles, run numerous parallel product lines, and demand quick iteration while maintaining a consistent user experience. In this context, micro frontends have emerged as a significant architectural option.

Microfrontends bring microservices concepts to web browsers. We divide it into smaller, independent applications that can be developed, deployed, and maintained separately rather than creating a single, massive, monolithic frontend. Better code ownership, quicker releases, and scalability are all promised by this design. But it's also complicated. It takes careful planning, robust governance, and an established development culture to implement micro frontends in a clean, production-safe manner.

Micro frontends are not something one implements just because they seem trending. They solve very specific organisational and technical challenges.

When Frontend Monoliths Become a Problem

A monolithic frontend starts becoming hard to manage when:

  • Teams grow beyond 8–10 developers.
    • More people touching the same codebase creates merge conflicts, inconsistent code quality, and slow onboarding.
  • Features require isolated release cycles.
    • One team’s changes often block another team’s deployment.
  • The build pipeline becomes slower.
    • Large builds and test suites increase CI/CD time.
  • Shared dependencies create version lock-in.
    • Upgrading Angular, RxJS, or UI libraries becomes a huge task affecting all teams.
  • Different parts of the application evolve at different speeds.
    • Some modules may require rapid iteration, others remain stable for years.

Micro frontends break this bottleneck by enabling each domain or feature to become an independently maintained slice of the UI.

What Micro Frontends Actually Mean

A micro frontend is not simply:

  • Multiple modules inside one Angular application
  • Lazy-loaded routes
  • A component library

Instead, a micro frontend is a fully independent application that owns:

  • Its own codebase
  • Its own state
  • Its own dependencies
  • Its own build pipeline
  • Its own deployment pipeline

It integrates into a larger shell application at runtime, not compile time.

Business Benefits
The architecture becomes worthwhile when:

  • You have multiple teams working on independent product domains
  • Release cycles vary by team
  • You want to scale development without rewriting everything
  • Stability and resilience matter
  • You want to test and deploy features independently
  • You want technology flexibility across teams

Micro frontends unlock long-term maintainability and organisational scalability.

2. Micro Frontend Architecture: The Big Picture
A typical micro frontend system includes:

  • A shell (container) application
  • Multiple micro frontend modules
  • A communication mechanism
  • A build and deployment strategy
  • A shared design system (optional but recommended)
  • Versioning and dependency governance

The Shell Application
The shell acts as the entry point of the system. Its responsibilities include:

  • Routing between micro frontends
  • Hosting shared dependencies
  • Bootstrapping the UI
  • Handling authentication and global state
  • Providing shared services (optional)

With Angular, this is often called the host or container application.

Micro Frontend Applications
Each micro frontend is responsible for:

  • Its own UI
  • Its own routing
  • Its own data management
  • Business logic for its domain
  • Independent deployments

A micro frontend must not depend on other micro frontends directly.

Integration Strategies
There are four dominant ways to integrate micro frontends:

  • Build-time integration (bad)
  • Server-side integration
  • Iframe-based integration
  • Runtime integration using Module Federation (modern and recommended)

Among these, Module Federation has become the de facto standard for micro frontends built using Angular, Webpack, or any modern frontend framework.

3. Angular + Module Federation: A Practical Approach
Angular 13+ introduced built-in support for Module Federation, which makes runtime integration smoother.

What Is Module Federation?

Module Federation is a Webpack feature (initially introduced with Webpack 5) that allows multiple independently built applications to load code from each other at runtime.

With Module Federation:

  • The shell can load Angular modules from remote applications
  • Micro frontends can expose specific modules
  • Each micro frontend can have its own Angular version (with caution)
  • No need to recompile or redeploy the shell when micro frontends update

How It Works?
Every micro frontend defines:

  • Expose configuration
  • RemoteEntry.js file containing the exported modules
  • Angular module to expose

The shell defines:

  1. Remote configuration pointing to each micro frontend
  2. Lazy-loaded routes consuming the exposed modules

When the system runs:

  • The shell loads the remote modules on demand via HTTP
  • Angular bootstraps the micro application inside the shell
  • Routing behaves as if the micro frontend is part of the shell

4. Setting Up a Micro Frontend Architecture in Angular
Let’s go step-by-step with a production-grade example.
Assume we are building a scalable e-commerce platform with teams for:

  • Catalogue
  • Cart
  • Checkout
  • User Profile

We will build:

  • A shell (host) application
  • Four micro frontends

Create the Angular Workspace

  • Each application should ideally live in its own Git repository.
  • However, during development, you can start with a monorepo.

ng new ecommerce-shell --routing --style=scss
ng new ecommerce-catalogue --routing --style=scss
ng new ecommerce-cart --routing --style=scss
ng new ecommerce-checkout --routing --style=scss
ng new ecommerce-profile --routing --style=scss

Add Module Federation
Install the toolkit:
ng add @angular-architects/module-federation

This command updates the Webpack configuration and adds a basic module federation setup.

Configure the Shell
Example webpack.config.js for the shell:
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  output: {
    uniqueName: "ecommerceShell",
  },
  plugins: [
    new ModuleFederationPlugin({
      remotes: {
        catalogue: "catalogue@http://localhost:4201/remoteEntry.js",
        cart: "cart@http://localhost:4202/remoteEntry.js",
        checkout: "checkout@http://localhost:4203/remoteEntry.js",
        profile: "profile@http://localhost:4204/remoteEntry.js",
      },
      shared: {
        "@angular/core": { singleton: true, strictVersion: true },
        "@angular/common": { singleton: true, strictVersion: true },
        "@angular/router": { singleton: true, strictVersion: true }
      },
    }),
  ],
};


Configure a Micro Frontend
Example for Catalogue micro frontend:
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  output: {
    uniqueName: "catalogue",
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "catalogue",
      filename: "remoteEntry.js",
      exposes: {
        "./Module": "./src/app/catalogue/catalogue.module.ts",
      },
      shared: {
        "@angular/core": { singleton: true },
        "@angular/common": { singleton: true },
        "@angular/router": { singleton: true }
      },
    }),
  ],
};


Routing in the Shell
The shell application loads each micro frontend lazily:
const routes: Routes = [
  {
    path: 'catalogue',
    loadChildren: () =>
      loadRemoteModule({
        remoteName: 'catalogue',
        exposedModule: './Module'
      }).then(m => m.CatalogueModule)
  },
  {
    path: 'cart',
    loadChildren: () =>
      loadRemoteModule({
        remoteName: 'cart',
        exposedModule: './Module'
      }).then(m => m.CartModule)
  }
];

This gives clean runtime integration.

5. Managing Shared Dependencies Across Micro Frontends
The Shared Dependencies Problem

Without governance:

  • Teams upgrade Angular independently
  • Shared libraries become misaligned
  • Bundle size increases
  • Runtime version conflicts break the UI

Best Practices

  • Use strictVersion for Angular packages.
  • Maintain a shared dependency contract across teams.
  • Use a central design system for UI components.
  • Establish versioning guidelines for shared libraries.

Angular Version Compatibility
Module Federation allows different versions of Angular, but this should be avoided unless:

  • Teams have strong reasons
  • You can guarantee compatibility
  • You are ready to handle runtime mismatches

The safest approach is to maintain a uniform version across all micro frontends.

6. Communication Between Micro Frontends

Micro frontends must be loosely coupled. Direct service injection across boundaries is not recommended.

Valid Communication Methods

  • URL-based communication
    • Encode identifiers in the route or query params.
    • This is the simplest and most stable approach.
  • Custom events (browser-native)
    • Suitable for parent-child communication inside the shell.
  • Shared service via shell dependency
    • Only when the service is stable and global (like Auth).
  • Cross-application state management
    • Only for limited use cases with clear governance.

Avoid

  • Direct Angular service injection between micro frontends
  • Shared mutable state
  • Synchronous coupling
  • Global event buses without versioning

7. Deployment Strategies
Independent Deployment Pipelines

Each micro frontend should:

  • Have its own CI/CD pipeline
  • Produce its own build artifact
  • Deploy to its own URL

The Shell Must Not Rebuild for Every Micro Frontend Release

The shell should only reference the remote entry URLs.

You must avoid:

  • Rebuilding the shell for micro frontend updates
  • Hardcoding versions in the shell
  • Self-Healing Deployments

Use:

  • Cache busting
  • Versioned remoteEntry files
  • Rollback support

8. Security and Authentication
The Shell Should Handle Authentication

Reasons:

  1. Prevent duplicate implementations
  2. Avoid inconsistent session logic
  3. Prevent insecure micro frontends
  4. Micro Frontends Use Tokens Provided by Shell

Options:

  1. Pass tokens via localStorage
  2. Use a shared Auth service exposed by shell
  3. Use cookies (with secure, HttpOnly flags)
  4. Route Guard Strategy

Use shell-based guards:

  • All protected routes go through shell-level checks
  • Micro frontends should not implement auth logic independently

9. Performance Considerations
When Micro Frontends Improve Performance

  • Parallel loading
  • Smaller independent bundles
  • Less re-rendering
  • Faster deployments

When They Hurt Performance

  • Too many remote loads
  • Overuse of shared dependencies
  • Fragmented UI leading to layout shifts

Solutions

  1. Use preloading strategies in the shell.
  2. Use shared libraries wisely.
  3. Cache remoteEntry files.

10. Real-World Best Practices
Start With Domain-Driven Boundaries
Each micro frontend should map to a self-contained business capability.

Avoid Over-Splitting
Micro frontends should be coarse-grained. Too many MFEs increase complexity.

Good example: Catalogue + Cart + Checkout + Profile
Bad example: Button micro frontend + Table micro frontend

Build a Strong Governance Model
Define:

  • Dependency rules
  • Angular version policy
  • Shared library update guidelines
  • Code quality expectations

Introduce Micro Frontends Gradually
Never rewrite the whole system into micro frontends at once.
Migrate module by module.

Monitoring
Use:

  • Application performance monitoring
  • Distributed logs
  • Error tracking per micro frontend

11. A Production Checklist
Before going live:

  • Stable Angular versions across MFEs
  • Central design system implemented
  • Shell handles authentication
  • Independent deployment pipelines configured
  • RemoteEntry files versioned
  • Routing tested for all MFEs
  • Error handling implemented
  • Performance benchmarks done
  • Security tests completed
  • Clear rollback strategy in place

12. Conclusion
Micro frontends offer a powerful way to scale frontend development in large organisations. They allow teams to work independently, release faster, and manage complexity with long-term sustainability. However, the architecture demands discipline, governance, and careful planning.

When implemented correctly with Angular and Module Federation, micro frontends provide:

  • Independent deployment
  • Domain isolation
  • Technology flexibility
  • Faster builds
  • Better team autonomy

In real world production systems, success depends less on framework capabilities and more on strong engineering practices, clear boundaries, and consistent dependency management. Micro frontends are not just a technical choice; they are an organisational strategy for scaling frontend teams responsibly.



AngularJS Hosting Europe - HostForLIFE :: Knowing Angular Observables Correctly

clock November 28, 2025 06:40 by author Peter

One of Angular's most misunderstood concepts is observables. When they hear words like stream, subscribe, pipe, operator, and async pipe, many novices get confused.
However, when described in basic, useful terms, observables are not difficult.

This article explains Observables in plain English, demonstrating how they function within Angular and how they are used in practical applications.

1. What Exactly Is an Observable?
An Observable is a data stream.
Think of it as a pipe through which data flows over time.

Examples of data that come in a stream:

  • API responses
  • Button clicks
  • Search input typing
  • WebSocket updates
  • Timer or interval updates

An Observable can send:

  • One value
  • Multiple values
  • No value
  • Or infinite values (like user clicks)

 

2. Observable vs Promise (Clear Difference)

FeatureObservablePromise
Multiple values Yes No
Can cancel Yes No
Lazy execution Yes No
Works with operators Yes No

Simple comparison

 

  • Promise gives you one response, like a courier delivering a parcel once.
  • Observable is like a live news channel, sending updates continuously.

 

3. How Angular Uses Observables Everywhere

 

Observables are built into Angular:

 

 

Angular FeatureUses Observables

HttpClient

Yes

Router events

Yes

Reactive Forms

Yes

Async Pipe

Yes

RxJS operators

Yes

WebSockets

Yes

So even if you don’t create an Observable yourself, Angular uses them internally.

4. Creating Your First Observable
This example sends three values and then completes.
const obs = new Observable(observer => {
  observer.next('Hello');
  observer.next('World');
  observer.next('Angular');
  observer.complete();
});


Subscribing:
obs.subscribe(value => console.log(value));

Output:
Hello
World
Angular

5. Using Observables With HttpClient (Most Common Case)

getUsers() {
  return this.http.get<User[]>('/api/users');
}


Calling the method:
this.userService.getUsers().subscribe(data => {
  this.users = data;
});


HttpClient returns an Observable because API data arrives asynchronously.

6. Understanding Subscription
Subscribing means:

  • Start listening to the Observable
  • Receive values
  • React to them

Example
const subscription = this.getUsers().subscribe(data => {
  console.log(data);
});


7. Why We Must Unsubscribe (Important)
If an Observable never completes (like events, WebSockets, intervals),
and you don't unsubscribe, memory leaks can occur.

Example
ngOnDestroy() {
  this.subscription.unsubscribe();
}


But Angular gives easier solutions: async pipe.

8. Using Async Pipe (Best Practice)
Instead of:
this.users$.subscribe(...)

Use in template:
<li *ngFor="let user of users$ | async">{{ user.name }}</li>

Benefits:

  • No unsubscribe required
  • Cleaner templates
  • Angular handles lifecycle automatically

9. RxJS Operators — Simple Explanation
Operators transform data inside the Observable pipeline.

Most used operators:

OperatorWhat it does
map Transform value
filter Remove unwanted values
debounceTime Delay emissions (good for search)
switchMap Cancel previous request and switch to new one
catchError Handle errors

Example search box:

this.searchControl.valueChanges.pipe(
  debounceTime(300),
  switchMap(text => this.api.search(text))
)
.subscribe(result => this.items = result);


10. Case Study: Building a Live Search
Step 1: Create a search form control
search = new FormControl('');

Step 2: Listen for typing
this.search.valueChanges.pipe(
  debounceTime(500),
  switchMap(keyword => this.service.search(keyword))
)
.subscribe(data => {
  this.results = data;
});

Typing fast will not spam the API.
switchMap automatically cancels previous calls.

11. Case Study: Auto-Refresh Dashboard Data

interval(5000).pipe(
  switchMap(() => this.api.getMetrics())
)
.subscribe(metrics => this.data = metrics);


This calls API every 5 seconds.

12. Case Study: Polling Until Condition Met
interval(1000).pipe(
  switchMap(() => this.api.getStatus()),
  filter(status => status === 'Completed')
)
.subscribe(() => console.log("Done"));

Real-world usage:
Tracking background job completion

Payment verification

File upload processing

13. Cold vs Hot Observables (Explained Simply)
Cold Observable
Starts fresh for each subscriber.

Example:
HttpClient, timers, intervals.
Hot Observable

Same stream shared with many subscribers.

Example:
User clicks, WebSocket messages.

14. Subjects: When You Need Manual Control
A Subject allows you to push values manually.
subject.next("Hello");

Use cases:

  • Component-to-component communication
  • Event bus
  • Live updates from WebSocket

15. BehaviorSubject (Most Useful in Apps)
Stores last emitted value.
user$ = new BehaviorSubject(null);

updateUser(u) {
  this.user$.next(u);
}

Useful for:

  • Authentication state
  • Theme state
  • Cart state

16. ReplaySubject
Replays previous values to new subscribers.

Used for:

  • Caching
  • Remembering previous events

17. Best Practices for Observables in Angular
Do

  • Use async pipe whenever possible
  • Use operators instead of nested subscriptions
  • Always unsubscribe for long-lived streams
  • Group multiple subscriptions using takeUntil
  • Keep Observables in services, not components

Avoid

  • Subscribing inside services (return the Observable instead)
  • Creating unnecessary Observables
  • Shadowing Observables with same names

18. Summary
Observables become easy when you understand:

  • They are streams of data
  • You subscribe to start listening
  • Operators modify the stream
  • Async pipe reduces boilerplate
  • Angular uses Observables everywhere

Observables are not complex. They are simply a powerful way to handle asynchronous programming in Angular.



AngularJS Hosting Europe - HostForLIFE :: Building an Angular Browser-Based Workflow Simulator (Drag Nodes → Simulate Execution)

clock November 21, 2025 07:43 by author Peter

One of the most helpful aspects for contemporary workplace apps is the creation of a browser-based workflow simulator. Custom workflows are frequently required in industries like manufacturing, aviation, banking, insurance, and IT operations. Before releasing the workflow to the real environment, users want to graphically build steps, connect nodes, establish rules, conduct simulations, and observe how the workflow works.

This article describes how to use Angular to create a Workflow Simulator that is suitable for production. The system facilitates:

The system supports:

  • Drag-and-drop node creation
  • Graph-based workflow builder
  • Branching paths and decision nodes
  • Node configuration panels
  • Execution simulation with event logs
  • Validation of loops, unreachable nodes, and broken paths
  • Pluggable rule engine
  • JSON-based storage and replay
  • Real-time visualisation of execution path

The goal is to build an Angular-based workflow editor that functions like lightweight versions of Camunda Modeler, Node-RED, or n8n, but fully customisable for your product.

Why Build a Workflow Simulator?
Typical enterprise systems contain long-running or multi-step business processes. Before executing workflows in production, developers and business users need a safe sandbox to test logic.

A browser-based simulator allows:

  • Quick iteration
  • Visual understanding of flows
  • Troubleshooting of decision logic
  • Validation before saving workflows
  • Replaying actual data
  • Training and demonstration


Most importantly, it reduces rework and production issues.

High-Level Features Required
A workflow simulator must include:

  • Node palette (Start, Task, Decision, API Call, Script, End).
  • Drag-and-drop canvas.
  • Connectors between nodes.
  • Node configuration (name, rules, conditions, inputs).
  • JSON export/import of the workflow.
  • Execution engine to simulate the workflow step-by-step.
  • Visual highlight of execution path.
  • Validation engine.
  • Undo/redo support.
  • Event logs and breakpoints.

This article focuses on the Angular implementation, core architecture, and execution simulation.

High-Level Architecture
The system is divided into:

  • Canvas Module
  • Nodes Module
  • Connection Engine
  • Workflow Engine (simulation)
  • Persistence Module
  • Validation Module
  • UI Components (palette, config panel)
Designing the Workflow Data Model
A workflow is a directed graph. Each node represents a step, and each edge represents the flow.

Node Structure
export interface WorkflowNode {
  id: string;
  type: 'start' | 'task' | 'decision' | 'api' | 'script' | 'end';
  name: string;
  position: { x: number; y: number };
  config?: any;
}
Edge Structure
export interface WorkflowEdge {
  id: string;
  from: string;
  to: string;
  condition?: string; // expression or rule
}
Workflow Definition
export interface WorkflowDefinition {
  nodes: WorkflowNode[];
  edges: WorkflowEdge[];
}

Building the Canvas in Angular

You will use these libraries/tools:
  • Angular CDK DragDrop Module
  • HTML canvas or SVG for rendering
  • RxJS for state management
  • A lightweight graph calculation library (optional)
Basic Canvas Component
@Component({
  selector: 'app-workflow-canvas',
  templateUrl: './workflow-canvas.component.html',
  styleUrls: ['./workflow-canvas.component.css']
})
export class WorkflowCanvasComponent {
  @Input() workflow!: WorkflowDefinition;

  onNodeDrag(event: CdkDragMove, node: WorkflowNode) {
    node.position.x = event.pointerPosition.x;
    node.position.y = event.pointerPosition.y;
  }
}

Canvas Template

<div class="canvas-area">
  <div *ngFor="let node of workflow.nodes"
       class="node"
       cdkDrag
       [style.left.px]="node.position.x"
       [style.top.px]="node.position.y">
       {{ node.name }}
  </div>

  <svg class="edge-layer">
    <ng-container *ngFor="let edge of workflow.edges">
      <line
        [attr.x1]="getNode(edge.from).position.x + 50"
        [attr.y1]="getNode(edge.from).position.y + 20"
        [attr.x2]="getNode(edge.to).position.x"
        [attr.y2]="getNode(edge.to).position.y"
        stroke="black"
        stroke-width="2" />
    </ng-container>
  </svg>
</div>

Implementing Drag-and-Drop Node Palette

A simple palette:
<div class="palette">
  <button (click)="addNode('task')">Add Task</button>
  <button (click)="addNode('decision')">Add Decision</button>
  <button (click)="addNode('api')">Add API Call</button>
</div>
Add Node Logic
addNode(type: string) {
  const newNode: WorkflowNode = {
    id: crypto.randomUUID(),
    type,
    name: `${type} node`,
    position: { x: 100, y: 100 }
  };

  this.workflow.nodes.push(newNode);
}

Adding Connectors Between Nodes

Common approach:
  • Click first node.
  • Click second node.
  • Create edge.
Add Edge Logic
addEdge(fromId: string, toId: string) {
  this.workflow.edges.push({
    id: crypto.randomUUID(),
    from: fromId,
    to: toId
  });
}

Adding Node Configuration Panel

This allows editing:
  • Name
  • Inputs
  • Conditions
  • Output fields
  • API parameters
  • Decision expressions
Sample Configuration Panel
<div class="config-panel" *ngIf="selectedNode">
  <h3>Node Configuration</h3>
  <label>Name:
    <input [(ngModel)]="selectedNode.name" />
  </label>

  <label *ngIf="selectedNode.type === 'decision'">
    Condition:
    <input [(ngModel)]="selectedNode.config.condition" />
  </label>
</div>

The Workflow Execution Engine

The Workflow Engine runs the workflow.
This includes:
  • Loading workflow JSON
  • Finding the start node
  • Executing each node
  • Evaluating rules
  • Logging events
  • Highlighting visited nodes
Engine Structure
export class WorkflowEngine {
  constructor(private workflow: WorkflowDefinition) {}

  private logs: string[] = [];

  execute() {
    let currentNode = this.findStartNode();

    while (currentNode && currentNode.type !== 'end') {
      this.logs.push(`Executing: ${currentNode.name}`);
      currentNode = this.getNextNode(currentNode);
    }

    this.logs.push('Reached end node');
    return this.logs;
  }
}


Rule Evaluation for Decision Nodes
Rules may be:
  • JavaScript expressions
  • JSON-based rule definitions
  • Condition strings
Example Rule Evaluation
evaluateCondition(condition: string, context: any): boolean {
  const fn = new Function("context", `return ${condition}`);
  return fn(context);
}

For large enterprise systems, use a safe rule engine instead of dynamic functions.

Simulating Execution Step-by-Step

Support:
  • Automatic play
  • Manual step-by-step
  • Breakpoints
Step Mode Example
step() {
  if (!this.currentNode) {
    this.currentNode = this.findStartNode();
    return this.currentNode;
  }

  this.currentNode = this.getNextNode(this.currentNode);
  return this.currentNode;
}


Visualising Execution Path
When the simulator runs:
  • Highlight the active node
  • Highlight the traversed edges
  • Dim inactive nodes
Canvas Highlighting Example
.node.active {
  border: 2px solid blue;
}

.edge.active {
  stroke: blue;
}

You will toggle CSS classes based on execution logs.

Validations Before Simulation
Essential validations:
  • Workflow must contain exactly one start node.
  • All nodes must be reachable from start.
  • No cycles unless allowed.
  • Decision nodes must contain valid conditions.
  • All edges must reference valid node IDs.
Example Validation Function
validateWorkflow(workflow: WorkflowDefinition): string[] {
  const errors: string[] = [];

  const startNodes = workflow.nodes.filter(n => n.type === 'start');
  if (startNodes.length !== 1) {
    errors.push('Workflow must contain exactly one start node.');
  }

  // Additional validations…

  return errors;
}

JSON Export and Import
The workflow editor must support saving and loading.

Export
downloadJSON() {
  const json = JSON.stringify(this.workflow);
  // download logic
}
Import
uploadWorkflow(json: string) {
  this.workflow = JSON.parse(json);
}

Undo / Redo Support

Use an RxJS BehaviorSubject to push state snapshots.

Example
history: WorkflowDefinition[] = [];
historyIndex = -1;

pushState() {
  this.history.push(JSON.parse(JSON.stringify(this.workflow)));
  this.historyIndex++;
}

Undo
undo() {
  if (this.historyIndex > 0) {
    this.historyIndex--;
    this.workflow = JSON.parse(JSON.stringify(this.history[this.historyIndex]));
  }
}

Real-World Challenges and Best Practices

Performance drops when rendering more than 200 nodes.
Use canvas or WebGL for large workflows.

Users need zooming and panning.
Implement with CSS transforms.

Complex connectors require bezier curves.
Use SVG path elements.

Decision rules get messy.
Integrate a rules engine (Nools, JSON-rules-engine).

Autosaving is essential.
Persist state in localStorage every few seconds.

Multi-user collaboration.
Use WebSockets and operational transforms.

Prevent invalid node drops.
Add guards and context checks.

Production-Level Recommendations
Use a state management library (NgRx or Akita) for storing workflow state.

Use RxJS Subjects for canvas events.

Debounce drag events to reduce change detection load.

Generate node IDs using crypto APIs for uniqueness.

Allow theming and custom node templates.

Include snap-to-grid behaviour for cleaner diagrams.

Create extension hooks for future custom nodes.

Conclusion

A browser-based workflow simulator is a powerful and reusable tool for any enterprise Angular application. By combining a graph-based canvas, configurable nodes, a robust execution engine, and a validation system, you can build a complete workflow modelling and testing system inside the browser. What makes this solution truly valuable is its flexibility. It doesn’t depend on any external engine and can be adapted for HR approvals, manufacturing processes, IT automation, financial rules, document processing, and more.


AngularJS Hosting Europe - HostForLIFE :: Using Angular +.NET to Create a Custom Rich Text Editor with Auto-Save and Mentions (@user)

clock November 19, 2025 08:11 by author Peter

Advanced text-editing features, such as inline formatting, auto-save drafts, mentions, and collaboration-ready structures, are becoming more and more necessary for modern workplace applications. Building your own lightweight Angular-based rich text editor gives you complete control, extensibility, and minimal dependency risk, whereas many teams rely on bulky third-party WYSIWYG editors.

DATABASE SCRIPT (SQL Server)
1. Users Table

CREATE TABLE Users (
    UserId INT IDENTITY PRIMARY KEY,
    DisplayName VARCHAR(100),
    Email VARCHAR(150)
);

2. DocumentDrafts Table

CREATE TABLE DocumentDrafts (
    DraftId INT IDENTITY PRIMARY KEY,
    DocumentId INT NOT NULL,
    Content NVARCHAR(MAX),
    LastSaved DATETIME DEFAULT GETDATE()
);

.NET BACKEND (ASP.NET Core 8 API)
1. Model

public class DocumentDraft
{
    public int DocumentId { get; set; }
    public string Content { get; set; }
}

2. Controller: EditorController.cs

[ApiController]
[Route("api/[controller]")]
public class EditorController : ControllerBase
{
    private readonly IConfiguration _config;

    public EditorController(IConfiguration config)
    {
        _config = config;
    }

    [HttpPost("saveDraft")]
    public async Task<IActionResult> SaveDraft([FromBody] DocumentDraft draft)
    {
        using SqlConnection con = new(_config.GetConnectionString("Default"));
        using SqlCommand cmd = new("IF EXISTS (SELECT 1 FROM DocumentDrafts WHERE DocumentId=@Id)
                                         UPDATE DocumentDrafts SET Content=@C, LastSaved=GETDATE() WHERE DocumentId=@Id
                                   ELSE
                                         INSERT INTO DocumentDrafts(DocumentId, Content) VALUES(@Id, @C)", con);

        cmd.Parameters.AddWithValue("@Id", draft.DocumentId);
        cmd.Parameters.AddWithValue("@C", draft.Content);

        con.Open();
        await cmd.ExecuteNonQueryAsync();

        return Ok(new { message = "Draft saved" });
    }

    [HttpGet("getDraft/{documentId}")]
    public async Task<IActionResult> GetDraft(int documentId)
    {
        using SqlConnection con = new(_config.GetConnectionString("Default"));
        using SqlCommand cmd = new("SELECT Content FROM DocumentDrafts WHERE DocumentId=@Id", con);

        cmd.Parameters.AddWithValue("@Id", documentId);

        con.Open();
        string? content = (string?)await cmd.ExecuteScalarAsync();

        return Ok(new { content });
    }
}

3. UsersController.cs for mentions

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IConfiguration _config;

    public UsersController(IConfiguration config)
    {
        _config = config;
    }

    [HttpGet("search")]
    public async Task<IActionResult> Search([FromQuery] string q)
    {
        using SqlConnection con = new(_config.GetConnectionString("Default"));
        using SqlCommand cmd = new("SELECT TOP 10 UserId, DisplayName FROM Users WHERE DisplayName LIKE @Q + '%'", con);

        cmd.Parameters.AddWithValue("@Q", q);

        con.Open();
        List<object> users = new();

        using SqlDataReader dr = await cmd.ExecuteReaderAsync();
        while (await dr.ReadAsync())
        {
            users.Add(new
            {
                UserId = dr.GetInt32(0),
                DisplayName = dr.GetString(1)
            });
        }

        return Ok(users);
    }
}

ANGULAR IMPLEMENTATION (FULL MODULE)
1. Module + Routing

editor.module.ts
@NgModule({
  declarations: [
    EditorComponent,
    MentionDropdownComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    RouterModule.forChild([
      { path: '', component: EditorComponent }
    ])
  ]
})
export class EditorModule {}


2. Editor Component UI
editor.component.html

<div class="toolbar">
  <button (click)="format('bold')">B</button>
  <button (click)="format('italic')">I</button>
  <button (click)="format('underline')">U</button>
</div>

<div class="editor-container">
  <div class="editor"
       contenteditable="true"
       (keyup)="onKeyUp($event)"
       (click)="closeMention()"
       #editor>
  </div>

  <app-mention-dropdown
      *ngIf="showMention"
      [items]="mentionResults"
      (select)="insertMention($event)">
  </app-mention-dropdown>
</div>


3. Editor Component TS
editor.component.ts

export class EditorComponent implements OnInit, OnDestroy {
  @ViewChild('editor') editor!: ElementRef;

  autoSaveInterval: any;
  showMention = false;
  mentionResults: any[] = [];
  mentionText = "";
  cursorPos = 0;

  constructor(private api: EditorService, private users: UsersService) {}

  ngOnInit(): void {
    this.loadDraft();
    this.startAutoSave();
  }

  format(cmd: string) {
    document.execCommand(cmd, false, '');
  }

  onKeyUp(event: KeyboardEvent) {
    const value = this.getContent();
    const char = event.key;

    if (char === '@') {
      this.showMention = true;
      this.mentionText = "";
    }

    if (this.showMention && char !== '@') {
      this.mentionText += char;
      this.searchUsers();
    }
  }

  searchUsers() {
    this.users.search(this.mentionText).subscribe(res => {
      this.mentionResults = res;
    });
  }

  insertMention(user: any) {
    document.execCommand("insertHTML", false, `<span class='mention'>@${user.displayName}</span>&nbsp;`);
    this.showMention = false;
  }

  getContent() {
    return this.editor.nativeElement.innerHTML;
  }

  loadDraft() {
    this.api.getDraft(1).subscribe(r => {
      if (r.content) this.editor.nativeElement.innerHTML = r.content;
    });
  }

  startAutoSave() {
    this.autoSaveInterval = setInterval(() => {
      const content = this.getContent();
      this.api.saveDraft({ documentId: 1, content }).subscribe();
    }, 3000);
  }

  ngOnDestroy() {
    clearInterval(this.autoSaveInterval);
  }

  closeMention() {
    this.showMention = false;
  }
}


4. Mention Dropdown Component
mention-dropdown.component.html

<div class="mention-box">
  <div *ngFor="let u of items" (click)="onSelect(u)">
      {{u.displayName}}
  </div>
</div>


mention-dropdown.component.ts
@Component({
  selector: 'app-mention-dropdown',
  templateUrl: './mention-dropdown.component.html'
})
export class MentionDropdownComponent {
  @Input() items: any[] = [];
  @Output() select = new EventEmitter<any>();

  onSelect(u: any) {
    this.select.emit(u);
  }
}

5. Services
editor.service.ts

@Injectable({ providedIn: 'root' })
export class EditorService {

  constructor(private http: HttpClient) {}

  saveDraft(model: any) {
    return this.http.post('/api/editor/saveDraft', model);
  }

  getDraft(id: number) {
    return this.http.get<any>(`/api/editor/getDraft/${id}`);
  }
}
users.service.ts

@Injectable({ providedIn: 'root' })
export class UsersService {
  constructor(private http: HttpClient) {}

  search(q: string) {
    return this.http.get<any[]>(`/api/users/search?q=${q}`);
  }
}
UI Styling (CSS)
editor.component.css
.toolbar button {
  margin-right: 6px;
  padding: 4px;
}

.editor {
  min-height: 300px;
  border: 1px solid #ccc;
  padding: 12px;
  border-radius: 5px;
}

.mention {
  background: #e3f2fd;
  color: #0277bd;
  padding: 2px 4px;
  border-radius: 4px;
}

.mention-box {
  position: absolute;
  background: white;
  border: 1px solid #ccc;
  width: 200px;
  z-index: 999;
  border-radius: 4px;
}

.mention-box div {
  padding: 6px;
  cursor: pointer;
}

.mention-box div:hover {
  background: #f1f1f1;
}



AngularJS Hosting Europe - HostForLIFE :: Using Web Workers to Optimize Angular Performance for Complex Calculations

clock November 13, 2025 09:09 by author Peter

Performance and responsiveness are important aspects of the user experience in contemporary single-page applications (SPAs). Although Angular is a strong framework for creating dynamic apps, the user interface (UI) may become sluggish or unresponsive when your application must manage sophisticated mathematical operations, data processing, or significant computations. This occurs because Angular's programming language, JavaScript, operates in a single thread, which means that data processing, user interactions, and UI updates all vie for the same execution line.


 In order to address this issue, browsers offer a robust feature called Web Workers, which enables you to execute background operations concurrently without interfering with the main thread.

In this article, we’ll explore how to use Web Workers in Angular, understand when to use them, and walk through a step-by-step implementation for improving app performance with real-world examples.

What Are Web Workers?
Web Workers are background scripts that run independently from the main JavaScript thread.
They allow you to perform CPU-intensive tasks — like image processing, data encryption, or large JSON transformations — without freezing your UI.

Key characteristics

  • Run in a separate thread (parallel to the main UI).
  • Communicate via message passing (using postMessage() and onmessage).
  • Have no direct access to DOM or global variables.
  • Can perform complex logic or data manipulation safely.

Example scenario
Imagine processing a large dataset of 100,000 records in an Angular app. Doing this directly in a component method can cause UI lag.
With a Web Worker, the processing happens in the background, and once completed, the result is sent back — keeping your UI smooth and responsive.

When Should You Use Web Workers?
Use Web Workers when:
You’re performing CPU-heavy or long-running tasks:

  • Mathematical computations
  • Image or video encoding
  • Parsing large JSON or XML files
  • Cryptographic or hashing operations

Your Angular app experiences frame drops or freezing during data operations.
You want to keep animations and interactions smooth while processing data in the background.

Avoid Web Workers when:

  • The task is lightweight or runs instantly.
  • You need direct DOM access.
  • The overhead of message passing outweighs benefits.

Step-by-Step Implementation in Angular
Let’s implement a practical example to understand Web Workers in Angular.

We’ll create a Prime Number Calculator — a CPU-heavy task that can easily freeze the UI if executed in the main thread.

Step 1: Create a New Angular Project
If you don’t already have one:
ng new web-worker-demo
cd web-worker-demo


Step 2: Generate a Web Worker
Angular CLI provides built-in support for workers:
ng generate web-worker app

You’ll be asked:
? Would you like to add Angular CLI support for Web Workers? Yes

Once done, Angular automatically:
Updates tsconfig.json with "webWorker": true

Creates a new file: src/app/app.worker.ts

Step 3: Write Logic in the Worker File
Open src/app/app.worker.ts and add the heavy computation logic.
/// <reference lib="webworker" />

// Function to find prime numbers up to a given limitfunction generatePrimes(limit: number): number[] {
  const primes: number[] = [];
  for (let i = 2; i <= limit; i++) {
    let isPrime = true;
    for (let j = 2; j * j <= i; j++) {
      if (i % j === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) primes.push(i);
  }
  return primes;
}

// Listen for messages from main threadaddEventListener('message', ({ data }) => {
  const primes = generatePrimes(data);
  postMessage(primes);
});

This worker listens for a message containing a number limit, computes prime numbers up to that limit, and sends them back to the main Angular thread.

Step 4: Modify the Component

Open src/app/app.component.ts:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div style="text-align:center; padding:20px;">
      <h2>Angular Web Worker Demo</h2>
      <input type="number" [(ngModel)]="limit" placeholder="Enter number" />
      <button (click)="calculate()">Generate Primes</button>
      <p *ngIf="loading">Calculating, please wait...</p>
      <div *ngIf="!loading && result.length">
        <h3>Prime Numbers:</h3>
        <p>{{ result.join(', ') }}</p>
      </div>
    </div>
  `,
})
export class AppComponent implements OnInit {
  limit = 100000;
  result: number[] = [];
  loading = false;
  worker!: Worker;

  ngOnInit(): void {
    if (typeof Worker !== 'undefined') {
      this.worker = new Worker(new URL('./app.worker', import.meta.url));
      this.worker.onmessage = ({ data }) => {
        this.result = data;
        this.loading = false;
      };
    } else {
      alert('Web Workers are not supported in this browser!');
    }
  }

  calculate() {
    this.loading = true;
    this.worker.postMessage(this.limit);
  }
}

Step 5: Enable FormsModule for ngModel
In app.module.ts, import the FormsModule:
import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FormsModule],
  bootstrap: [AppComponent],
})
export class AppModule {}


Step 6: Run the Application
Run the Angular app:
ng serve

Open the browser at http://localhost:4200 and enter a large number like 100000.
Without Web Workers, the UI would freeze; now it remains smooth while computation happens in the background.

How It Works?

  • When the user clicks Generate Primes, the component sends a message to the Web Worker using postMessage().
  • The worker executes generatePrimes() in a separate thread.
  • Once computation finishes, the worker sends results back using postMessage().
  • The Angular component receives the result via onmessage and updates the UI.
Error Handling in Workers
You can also handle runtime errors gracefully.
this.worker.onerror = (error) => {
  console.error('Worker error:', error);
  this.loading = false;
};

Always include fallback logic if a browser doesn’t support Web Workers.

Terminating a Worker
If a user cancels an operation midway, terminate the worker:
if (this.worker) {
  this.worker.terminate();
}

This ensures memory is freed and no unnecessary computation continues in the background.

Advanced Example: JSON Data Processing
Suppose your Angular app downloads a 50MB JSON file and you want to filter and aggregate data efficiently.
Worker (data.worker.ts)

addEventListener('message', ({ data }) => {
  const result = data.filter((x: any) => x.isActive);
  postMessage(result.length);
});

Component
this.worker.postMessage(largeJsonArray);
this.worker.onmessage = ({ data }) => {
  console.log('Active records count:', data);
};

The computation runs in the worker thread, keeping your UI smooth.

Combining Web Workers with RxJS
You can wrap the Web Worker communication in an RxJS Observable for a cleaner and reactive design.
calculatePrimes(limit: number): Observable<number[]> {
  return new Observable((observer) => {
    const worker = new Worker(new URL('./app.worker', import.meta.url));
    worker.onmessage = ({ data }) => {
      observer.next(data);
      observer.complete();
      worker.terminate();
    };
    worker.onerror = (err) => observer.error(err);
    worker.postMessage(limit);
  });
}

This allows seamless integration with Angular’s reactive programming pattern.

Best Practices for Using Web Workers in Angular
Use Workers for CPU-Intensive Tasks Only
Avoid creating unnecessary workers for small operations.

Limit the Number of Workers
Each worker consumes memory; don’t overload the browser.

Terminate Workers When Not Needed
Prevent memory leaks by calling worker.terminate().

Serialize Data Efficiently
Minimize payload size when using postMessage().

Use SharedArrayBuffer (if needed)
For high-performance use cases, shared memory can reduce data transfer overhead.

Profile Performance
Use Chrome DevTools → Performance tab to measure improvement.

Integration with ASP.NET Core Backend
While Web Workers run in the browser, you can integrate them with your ASP.NET Core backend to optimize client-server performance.
For example:

The worker can pre-process data (filter, aggregate) before sending it to the API.

The API only receives minimal, structured data.

This combination reduces network payloads and API processing time — improving overall system efficiency.

Conclusion

Web Workers are one of the most underutilized features in frontend development. For Angular applications dealing with heavy computations or large data processing, using Web Workers can dramatically enhance performance and user experience. They ensure the main UI thread remains responsive, users experience smooth interactions, and complex tasks run efficiently in parallel.

By implementing Web Workers effectively — and combining them with Angular’s reactive ecosystem — developers can build high-performance, scalable web apps that deliver a desktop-like experience, even for complex workloads.


AngularJS Hosting Europe - HostForLIFE :: Using Angular 19 with ASP.NET Core 9 to Create a Scalable Web Application

clock November 12, 2025 07:12 by author Peter

The aim of every developer in contemporary web development is to create an application that is high-performing, scalable, and maintained.
A robust, modular, and cloud-ready full-stack solution may be achieved by combining Angular 19 for the frontend and ASP.NET Core 9 for the backend.

Developers looking for a realistic, hands-on approach covering everything from architecture setup to production deployment should read this article.

Why ASP.NET Core 9 + Angular 19?

FeatureAngular 19 (Frontend)ASP.NET Core 9 (Backend)
Language TypeScript C#
Rendering SSR + CSR + Hydration API-first
Build System Standalone components, Signals, ESBuild Minimal APIs, gRPC, Native AOT
Performance Improved reactivity model Optimized for microservices
Dev Tools Angular CLI 19, Vite .NET CLI, EF Core 9
Ideal Use SPAs, PWAs REST APIs, Web APIs, Services

Angular handles rich UI and real-time interaction, while ASP.NET Core delivers high-speed APIs and scalable backend logic.

Step 1: Architecture Design
A scalable architecture clearly delineates roles.

Step 2: Setting up the Backend (ASP.NET Core 9)
Create the API Project
dotnet new webapi -n ScalableApp.Api
cd ScalableApp.Api


Example: Model Class
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}


Example: Repository Pattern
public interface IProductRepository
{
    Task<IEnumerable<Product>> GetAllAsync();
    Task<Product?> GetByIdAsync(int id);
    Task AddAsync(Product product);
}

public class ProductRepository : IProductRepository
{
    private readonly AppDbContext _context;
    public ProductRepository(AppDbContext context) => _context = context;

    public async Task<IEnumerable<Product>> GetAllAsync() => await _context.Products.ToListAsync();
    public async Task<Product?> GetByIdAsync(int id) => await _context.Products.FindAsync(id);
    public async Task AddAsync(Product product)
    {
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
    }
}


Example: Controller
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
    private readonly IProductRepository _repository;
    public ProductController(IProductRepository repository) => _repository = repository;

    [HttpGet]
    public async Task<IActionResult> GetAll() => Ok(await _repository.GetAllAsync());
}


Step 3: Connect to SQL Server (EF Core 9)
Install EF Core
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Setup DbContext

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }

    public DbSet<Product> Products => Set<Product>();
}

Register in Program.cs
builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IProductRepository, ProductRepository>();


Example appsettings.json
{"ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=ScalableAppDB;Trusted_Connection=True;"}}


Step 4: Building the Frontend (Angular 19)
Create Angular App

ng new scalable-app --standalone
cd scalable-app
ng serve

Install Required Packages
npm install @angular/material @angular/forms @angular/common rxjs

Create a Product Service
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Product {
  id: number;
  name: string;
  price: number;
}

@Injectable({ providedIn: 'root' })
export class ProductService {
  private apiUrl = 'https://localhost:5001/api/product';

  constructor(private http: HttpClient) {}

  getAll(): Observable<Product[]> {
    return this.http.get<Product[]>(this.apiUrl);
  }
}


Display Products in Component
import { Component, OnInit, signal } from '@angular/core';
import { ProductService, Product } from '../services/product.service';

@Component({
  selector: 'app-product-list',
  standalone: true,
  template: `
    <h2>Product List</h2>
    <ul>
      <li *ngFor="let p of products()">
        {{ p.name }} - {{ p.price | currency }}
      </li>
    </ul>
  `
})
export class ProductListComponent implements OnInit {
  products = signal<Product[]>([]);

  constructor(private service: ProductService) {}

  ngOnInit() {
    this.service.getAll().subscribe(res => this.products.set(res));
  }
}


Step 5: Add Authentication (JWT + Angular Guard)
Backend (ASP.NET Core)

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "https://yourapi.com",
            ValidAudience = "https://yourapp.com",
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    });

Frontend (Angular 19 Guard)
import { CanActivateFn } from '@angular/router';
export const authGuard: CanActivateFn = () => {
  const token = localStorage.getItem('token');
  return !!token;
};

Step 6: Deployment Strategy
Angular Build for Production

ng build --configuration production

The build output will be in /dist/scalable-app.

ASP.NET Core Publish

dotnet publish -c Release -o ./publish

Host Both Together
Place Angular’s built files inside ASP.NET Core’s wwwroot folder.
Modify Program.cs:
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapFallbackToFile("index.html");

Step 7: Best Practices for Scalability

AreaBest Practice
API Use async methods and pagination
Database Use stored procedures for heavy queries
Caching Add MemoryCache / Redis for repeated API calls
Logging Centralize logs with Serilog / Application Insights
Security Use HTTPS, JWT, and CORS configuration
Frontend Lazy load routes and use Angular Signals
DevOps Use CI/CD pipelines (GitHub Actions / Azure DevOps)

Step 8: CI/CD Integration
Example GitHub Actions pipeline for .NET + Angular:
name: Build and Deploy
on:push:
    branches: [ main ]

jobs:build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'
      - name: Build .NET API
        run: dotnet publish ./ScalableApp.Api -c Release -o ./publish

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Build Angular
        run: |
          cd scalable-app
          npm ci
          npm run build

Full-Stack Flow Diagram
[ Angular 19 UI ]
            |
(HTTP calls via HttpClient)
            ↓
   [ ASP.NET Core 9 API ]
            |
   [ Business Logic Layer ]
            |
   [ EF Core Repository ]
            |
   [ SQL Server Database ]


Conclusion
By combining Angular 19 and ASP.NET Core 9, you can build a robust, modular, and enterprise-grade web application that scales effortlessly.

Key takeaways:
Use layered architecture for clean separation of concerns.
Integrate EF Core 9 for fast database operations.
Apply JWT authentication for secure communication.
Deploy via CI/CD pipelines for efficiency and reliability.
With this setup, your web app is ready for both enterprise growth and modern cloud environments.



AngularJS Hosting Europe - HostForLIFE :: Using Angular Standalone Components and Signal APIs to Create High-Performance User Interfaces

clock November 4, 2025 06:45 by author Peter

Angular has made significant progress in recent years to improve runtime performance and simplify app structure. Developers can now create apps that load more quickly, operate more smoothly, and require less maintenance thanks to Angular Standalone Components and the new Signal API. This post will explain these capabilities, their significance, and how to combine them to create cutting-edge, lightning-fast Angular applications.

1. Standalone Components: What Are They?
In the past, each Angular component required to be a part of a NgModule.
However, you can create Standalone Components starting with Angular 14 and now fully stable with Angular 17+, so they don't need to be specified inside a module.

Example: Creating a Standalone Component
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [CommonModule],
  template: `<h2>Welcome to Dashboard!</h2>`
})
export class DashboardComponent {}


No @NgModule needed!
You can directly use this component in routing or even bootstrap it in main.ts.

2. Bootstrapping with Standalone Components
With standalone components, even your AppModule becomes optional.
Example:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent)
  .catch(err => console.error(err));


That’s it no AppModule required!
This reduces overhead and speeds up the app’s initial load time.

3. Angular Signal API: The Game Changer

The Signal API, introduced in Angular 16+, is a powerful new way to manage reactive state without complex libraries like RxJS or NgRx.

Signals are reactive variables that automatically update the UI whenever their value changes.

Example: Simple Counter Using Signal
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <h2>Count: {{ count() }}</h2>
    <button (click)="increment()">Increment</button>
  `
})
export class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(c => c + 1);
  }
}


No BehaviorSubject, no subscriptions just simple, reactive code.

4. How Signals Improve Performance
With traditional change detection, Angular re-renders components unnecessarily.
Signals fix this by using fine-grained reactivity — only updating parts of the UI that actually change.

This means:

  • Less re-rendering
  • Better performance
  • Cleaner codebase

5. Combining Standalone Components and Signals
Together, Standalone Components and Signals make Angular apps simpler and more efficient.

Here’s an example of how both can be used in a real-world scenario like a Product Dashboard.
Example: Product Dashboard with Reactive State
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-product-dashboard',
  standalone: true,
  imports: [CommonModule],
  template: `
    <h1>Product Dashboard</h1>
    <input type="text" placeholder="Search product..." (input)="search($event)">
    <ul>
      <li *ngFor="let p of filteredProducts()">{{ p }}</li>
    </ul>
  `
})
export class ProductDashboardComponent {
  products = signal(['TV', 'Fridge', 'Laptop', 'Fan', 'Microwave']);
  filteredProducts = signal(this.products());

  search(event: any) {
    const keyword = event.target.value.toLowerCase();
    this.filteredProducts.set(
      this.products().filter(p => p.toLowerCase().includes(keyword))
    );
  }
}


Here:

  • Signals manage product lists.
  • Standalone Component keeps the code modular and fast.
  • The UI updates instantly without manual subscriptions.

6. Flowchart: How It Works
Below is a simple visual flow for your blog (you can design this using Canva or draw.io):

User Action (e.g., Click or Input)
          ↓
 Signal Updates (state change)
          ↓
 Angular detects signal change
          ↓
 Component Re-renders (only affected part)
          ↓
 UI Updates Instantly

This shows how Signals streamline UI updates with minimal re-rendering.

7. Responsive UI Example with PrimeNG

Now let’s combine Angular + PrimeNG to make a clean, responsive dashboard.
Example UI Structure

-------------------------------------
| Header: App Title + Menu Button   |
-------------------------------------
| Sidebar |     Main Dashboard      |
|         |  - Charts               |
|         |  - Stats Cards          |
|         |  - Product List         |
-------------------------------------

PrimeNG Components Used:
p-card for summary boxes
p-table for data grids
p-chart for performance visualization
p-sidebar for navigation

Example Snippet
<p-sidebar [(visible)]="menuVisible">
  <h3>Menu</h3>
  <p>Dashboard</p>
  <p>Reports</p>
</p-sidebar>

<p-card header="Total Sales">
  <h2>{{ totalSales() | currency }}</h2>
</p-card>

<p-chart type="bar" [data]="chartData()"></p-chart>

Your app becomes lighter, more modular, and boots up more quickly as a result. 
This gives a smooth, mobile-friendly dashboard that responds instantly due to Signals.

8. Performance Comparison

FeatureBefore (RxJS/Modules)Now (Signals/Standalone)

App Bootstrap

Slower

Faster

State Management

Complex

Simple

Change Detection

Broad

Fine-grained

Code Size

Larger

Smaller

Learning Curve

Steep

Easier

9. Real-World Benefits

  • Faster App Loading
  • Simplified Codebase
  • No Extra Libraries
  • Better Reusability
  • Improved UI Responsiveness

10. Conclusion
Angular is now quicker, lighter, and easier for developers to use thanks to Standalone Components and Signals. These capabilities allow you create contemporary, high-performance user interfaces with clear, reactive logic while streamlining structure and improving speed. Now is the ideal moment to update your Angular projects if you haven't already.



AngularJS Hosting Europe - HostForLIFE :: Database-Based Dynamic ActiveX Report in Angular

clock October 27, 2025 08:34 by author Peter

ActiveX reports are still used to generate and show comprehensive business reports (such as invoices, financial summaries, and logs) in a large number of enterprise-grade web applications, particularly those that are moving from legacy systems. Even though Internet Explorer has historically used ActiveX controls, you may still use other integration strategies to embed, create, and dynamically bind ActiveX reports from a database in contemporary Angular apps, like:

  • Using a COM-based backend API (C#/.NET) to render and serve reports
  • Embedding report viewers (like Crystal Reports, Stimulsoft, or ActiveReports) via iframe or custom web components
  • Fetching data dynamically via Angular services (HttpClient)

This article explains how to generate and display dynamic ActiveX-based reports from a SQL database in an Angular frontend.

Prerequisites
Before you start, ensure you have the following setup:

  • Angular 17+
  • .NET 6+ or .NET Framework 4.8 (for backend ActiveX/COM integration)
  • SQL Server Database
  • ActiveX or reporting tool (e.g., ActiveReports, Crystal Reports, or COM Report Engine)
  • Node.js and npm installed

Architecture Overview
The flow for generating the dynamic ActiveX report is as follows:
[Angular App] ---> [Web API / .NET Backend] ---> [Database (SQL Server)]
                                           ---> [ActiveX Report Engine]

  • Angular app sends a request (with parameters) to the backend.
  • Backend retrieves data from SQL Server.
  • Backend generates a report (ActiveX / COM report file).
  • Angular displays the generated report (via iframe or viewer component).

Step 1. Create a Backend API to Generate Reports
You can use a C# .NET Web API to handle the ActiveX or report engine integration.

Here’s a simple example of a backend controller (ReportController.cs):
[ApiController]
[Route("api/[controller]")]
public class ReportController : ControllerBase
{
    private readonly IConfiguration _config;

    public ReportController(IConfiguration config)
    {
        _config = config;
    }

    [HttpGet("GenerateReport")]
    public IActionResult GenerateReport(int reportId)
    {
        string connectionString = _config.GetConnectionString("DefaultConnection");

        // Fetch data from SQL Server
        var data = new DataTable();
        using (SqlConnection con = new SqlConnection(connectionString))
        {
            string query = "SELECT * FROM Sales WHERE ReportId = @ReportId";
            using (SqlCommand cmd = new SqlCommand(query, con))
            {
                cmd.Parameters.AddWithValue("@ReportId", reportId);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(data);
            }
        }

        // Use COM-based ActiveX Report Engine (e.g., ActiveReports)
        var report = new ActiveXReportLib.ReportClass();
        report.LoadTemplate("C:\\Reports\\SalesReport.rpt");
        report.SetDataSource(data);
        string pdfPath = $"C:\\Reports\\Output\\Report_{reportId}.pdf";
        report.ExportToPDF(pdfPath);

        return File(System.IO.File.ReadAllBytes(pdfPath), "application/pdf");
    }
}

This code:

  • Connects to SQL Server
  • Loads an ActiveX-based report template
  • Fills it with data
  • Exports it as PDF
  • Returns it to the frontend

Step 2. Create an Angular Service to Fetch the Report
In Angular, create a ReportService to fetch the generated report.

report.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ReportService {
  private apiUrl = 'https://localhost:5001/api/Report';

  constructor(private http: HttpClient) {}

  getReport(reportId: number) {
    return this.http.get(`${this.apiUrl}/GenerateReport?reportId=${reportId}`, { responseType: 'blob' });
  }
}


Step 3. Display Report in Angular Component
Now, create a component to display the generated report (PDF).

report-viewer.component.ts
import { Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { ReportService } from './report.service';

@Component({
  selector: 'app-report-viewer',
  templateUrl: './report-viewer.component.html',
  styleUrls: ['./report-viewer.component.css']
})
export class ReportViewerComponent {
  pdfSrc: any;

  constructor(private reportService: ReportService, private sanitizer: DomSanitizer) {}

  loadReport() {
    const reportId = 101; // dynamic ID based on user selection
    this.reportService.getReport(reportId).subscribe((data) => {
      const blob = new Blob([data], { type: 'application/pdf' });
      const url = URL.createObjectURL(blob);
      this.pdfSrc = this.sanitizer.bypassSecurityTrustResourceUrl(url);
    });
  }
}


report-viewer.component.html
<div class="report-container">
  <button (click)="loadReport()" class="btn btn-primary">Generate Report</button>

  <iframe *ngIf="pdfSrc" [src]="pdfSrc" width="100%" height="600px"></iframe>
</div>


Step 4. Style and Integration
You can enhance the user interface by integrating Bootstrap or Angular Material:

npm install bootstrap

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


Step 5. Dynamic Report Parameters
You can allow users to select filters (like date range, department, or region) and pass them to the API dynamically:
this.reportService.getReportByParams({ startDate, endDate, region });

Your backend can then use these parameters in SQL queries or stored procedures to fetch dynamic data.

Conclusion

By combining Angular’s dynamic frontend capabilities with a .NET backend that interfaces with ActiveX or COM-based report engines, you can:
Generate dynamic reports using real-time database data
Export reports in formats like PDF or Excel
Integrate modern UI controls while preserving legacy ActiveX report compatibility
This hybrid approach enables organizations to modernize their reporting systems without completely discarding older but reliable ActiveX reporting engines.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in