Full Trust European Hosting

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

AngularJS Hosting Europe - HostForLIFE :: Custom Deferred Grid Using MVC Web API And AngularJS

clock June 26, 2025 10:09 by author Peter

This post will demonstrate how you use AngularJS and Web API to create a custom deferred grid in MVC. Using web API and AngularJS $http services, we will retrieve the data from the database and create a unique user interface for the grid. When working with a Web API, we typically utilize the ADO.NET Entity data model as the model class, correct?

Here, we will retrieve the data from our database using our standard SQLDataAdapter, SQLConnection, and stored procedure in place of an entity data model. We use Virtual Repeat in AngularJS for loading the data in UI, so that the data will be loaded whenever there is a user action that is scrolling (Virtual Scrolling). So that in the view port we will load only few items first. Now shall we go and see this in detail? I hope you will like this.

Background
We have so many plugins available to show the data in a grid format, don't we? if you want to know few of them, you can find them here. Now what if you need to show the data in a grid format without using any additional plugins? What if you need to load the data to that grid dynamically, that is whenever user scrolls the grid? If you could not find the answer for these questions, here in this post I am going to share an option. I hope you will enjoy reading.

Create a MVC application
Click File, New, Project and then select MVC application. Before going to start the coding part, make sure that AngularJS is installed. You can see all the items mentioned above from NuGet. Right click on your project name and select Manage NuGet packages. 

AngularJS NuGet Package Visual Studio
Once you have installed, please make sure that all the items are loaded in your scripts folder.

Using the code
I hope everything is set now, then it is time to start our coding. First we will create a controller action and a view. Below is the code snippet of our controller.
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Web;  
    using System.Web.Mvc;  
    namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Controllers  
    {  
        public class DefaultController: Controller  
        {  
            // GET: Default  
            public ActionResult Index()  
            {  
                return View();  
            }  
        }  
    }  


Here Default/em> is my controller name. Now create a view for this action, and load the needed references.
    @{  
    ViewBag.Title = "Index";  
    }  
    <h2>Index</h2>  
    <link href="~/Content/angular-material.css" rel="stylesheet" />  
    <script src="~/scripts/angular.min.js"></script>  
    <script src="~/scripts/angular-route.min.js"></script>  
    <script src="~/scripts/angular-aria.min.js"></script>  
    <script src="~/scripts/angular-animate.min.js"></script>  
    <script src="~/scripts/angular-messages.min.js"></script>  
    <script src="~/scripts/angular-material.js"></script>  
    <script src="~/scripts/svg-assets-cache.js"></script>  
    <script src="~/scripts/Default/Default.js"></script>  


You can get these files from the source code attached with this article. And the file Default.js is the additional file where we are requested to do our additional scripts. So far the basic implementation of our view is done. Now we will create a Web API and additional model class to fetch the data from the database. Are you ready? 

Below is my Web API controller.
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Net;  
    using System.Net.Http;  
    using System.Web.Http;  
    using Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models;  
    namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Controllers  
    {  
        public class DataAPIController : ApiController  
        {  
          DataModel dm = new DataModel();  
          public string getData(int id)  
            {  
              var d = dm.fetchData(id);  
              return d;  
            }  
        }  
    }  


Have you noticed that I have included using Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models; in the section? This is to ensure that we can use the model classes whatever we have created so far. In this case DataModel is our model class and we are creating an instance for the same.
    DataModel dm = new DataModel();  

The controller action getData accepting the parameter id, right? This is actually the page offset value which we are passing from the client side. Now we will create our model class, you can find the code snippets below for that.
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Web;  
    using System.Data;  
    using System.Data.Sql;  
    using System.Data.SqlClient;  
    using System.Configuration;  
    namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models  
    {  
        public class DataModel  
        {  
        public string fetchData(int pageOffset)  
        {  
            string connection = ConfigurationManager.ConnectionStrings["TrialsDBEntities"].ConnectionString;  
            using (SqlConnection cn = new SqlConnection(connection))  
            {  
            SqlCommand cmd = new SqlCommand("usp_Get_SalesOrderDetailPage", cn);  
            cmd.Parameters.Add("@pageoffset", SqlDbType.Int).Value = pageOffset;  
            cmd.CommandType = CommandType.StoredProcedure;  
            try  
                {  
                DataTable dt = new DataTable();  
                SqlDataAdapter da = new SqlDataAdapter(cmd);  
                cn.Open();  
                da.Fill(dt);  
                return GetJson(dt);  
                }  
            catch (Exception)  
            {  
            throw;  
            }  
          }  
        }  
     }  
    }


As I said before, instead of using an entity model we use our normal sql connections and sql data adapter to load the data. Before going to use this function, please make sure that you have added the below references.
    using System.Data;  
    using System.Data.Sql;  
    using System.Data.SqlClient;  
    using System.Configuration;


Now coming back to the fetchData function, we use the connection string TrialsDBEntities from the web config file. So it is mandatory that you must have a connection string with that name in your web config file. Once that is done, we call the stored procedure usp_Get_SalesOrderDetailPage and fill the data using SqlDataAdapter. 

Another thing to be notified here is we are passing that DataTable to a function called GetJson. So you must have the definition for that too.
    public string GetJson(DataTable dt)  
    {  
     try  
     {  
       if (dt == null)  
        {  
          throw new ArgumentNullException("dt");  
        }  
       System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();  
       List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();  
       Dictionary<string, object> row = null;  
       foreach (DataRow dr in dt.Rows)  
       {  
        row = new Dictionary<string, object>();  
        foreach (DataColumn col in dt.Columns)  
         {  
           row.Add(col.ColumnName.Trim(), dr[col]);  
         }  
         rows.Add(row);  
       }  
    return serializer.Serialize(rows);  
    }  
    catch (Exception)  
    {  
    throw;  
    }  
    } 


What this function does is, it converts the data table to a JSON format. So far, the coding related to Web API is done, now it is time to create a database, table, and a stored procedure. 

Create a database
The following query can be used to create a database in your SQL Server.
    USE [master]  
    GO  
    /****** Object: Database [TrialsDB] Script Date: 25-Feb-16 12:34:32 PM ******/  
    CREATE DATABASE [TrialsDB]  
    CONTAINMENT = NONE  
    ON PRIMARY  
    ( NAME = N'TrialsDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )  
    LOG ON  
    ( NAME = N'TrialsDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)  
    GO  
    ALTER DATABASE [TrialsDB] SET COMPATIBILITY_LEVEL = 110  
    GO  
    IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))  
    begin  
    EXEC [TrialsDB].[dbo].[sp_fulltext_database] @action = 'enable'  
    end  
    GO  
    ALTER DATABASE [TrialsDB] SET ANSI_NULL_DEFAULT OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET ANSI_NULLS OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET ANSI_PADDING OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET ANSI_WARNINGS OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET ARITHABORT OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET AUTO_CLOSE OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET AUTO_CREATE_STATISTICS ON  
    GO  
    ALTER DATABASE [TrialsDB] SET AUTO_SHRINK OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS ON  
    GO  
    ALTER DATABASE [TrialsDB] SET CURSOR_CLOSE_ON_COMMIT OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET CURSOR_DEFAULT GLOBAL  
    GO  
    ALTER DATABASE [TrialsDB] SET CONCAT_NULL_YIELDS_NULL OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET NUMERIC_ROUNDABORT OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET QUOTED_IDENTIFIER OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET RECURSIVE_TRIGGERS OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET DISABLE_BROKER  
    GO  
    ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS_ASYNC OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET DATE_CORRELATION_OPTIMIZATION OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET TRUSTWORTHY OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET ALLOW_SNAPSHOT_ISOLATION OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET PARAMETERIZATION SIMPLE  
    GO  
    ALTER DATABASE [TrialsDB] SET READ_COMMITTED_SNAPSHOT OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET HONOR_BROKER_PRIORITY OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET RECOVERY FULL  
    GO  
    ALTER DATABASE [TrialsDB] SET MULTI_USER  
    GO  
    ALTER DATABASE [TrialsDB] SET PAGE_VERIFY CHECKSUM  
    GO  
    ALTER DATABASE [TrialsDB] SET DB_CHAINING OFF  
    GO  
    ALTER DATABASE [TrialsDB] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )  
    GO  
    ALTER DATABASE [TrialsDB] SET TARGET_RECOVERY_TIME = 0 SECONDS  
    GO  
    ALTER DATABASE [TrialsDB] SET READ_WRITE  
    GO 


Now we will create a table. 

Create table in database
Below is the query to create table in database.
    USE [TrialsDB]  
    GO  
    /****** Object: Table [dbo].[SalesOrderDetail] Script Date: 25-Feb-16 12:35:45 PM ******/  
    SET ANSI_NULLS ON  
    GO  
    SET QUOTED_IDENTIFIER ON  
    GO  
    CREATE TABLE [dbo].[SalesOrderDetail](  
    [SalesOrderID] [int] NOT NULL,  
    [SalesOrderDetailID] [int] IDENTITY(1,1) NOT NULL,  
    [CarrierTrackingNumber] [nvarchar](25) NULL,  
    [OrderQty] [smallint] NOT NULL,  
    [ProductID] [int] NOT NULL,  
    [SpecialOfferID] [int] NOT NULL,  
    [UnitPrice] [money] NOT NULL,  
    [UnitPriceDiscount] [money] NOT NULL,  
    [LineTotal] AS (isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0))),  
    [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL,  
    [ModifiedDate] [datetime] NOT NULL,  
    CONSTRAINT [PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID] PRIMARY KEY CLUSTERED  
    (  
    [SalesOrderID] ASC,  
    [SalesOrderDetailID] ASC  
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
    ) ON [PRIMARY]  
    GO  


Can we insert some data to the table now?

Insert data to table
To insert the data, I will attach a database script file along with the download file, you can either run that or insert some data using the following query. By the way if you would like to know how to generate scripts with data in SQL Server, you can check here.
    USE [TrialsDB]  
    GO  
    INSERT INTO [dbo].[SalesOrderDetail]  
    ([SalesOrderID]  
    ,[CarrierTrackingNumber]  
    ,[OrderQty]  
    ,[ProductID]  
    ,[SpecialOfferID]  
    ,[UnitPrice]  
    ,[UnitPriceDiscount]  
    ,[rowguid]  
    ,[ModifiedDate])  
    VALUES  
    (<SalesOrderID, int,>  
    ,<CarrierTrackingNumber, nvarchar(25),>  
    ,<OrderQty, smallint,>  
    ,<ProductID, int,>  
    ,<SpecialOfferID, int,>  
    ,<UnitPrice, money,>  
    ,<UnitPriceDiscount, money,>  
    ,<rowguid, uniqueidentifier,>  
    ,<ModifiedDate, datetime,>)  
    GO

Along with this, we can create a new stored procedure which will fetch the data. The following is the query to create the stored procedure.
    USE [TrialsDB]  
    GO  
    /****** Object: StoredProcedure [dbo].[usp_Get_SalesOrderDetailPage] Script Date: 25-Feb-16 12:53:07 PM ******/  
    SET ANSI_NULLS ON  
    GO  
    SET QUOTED_IDENTIFIER ON  
    GO  
    -- =============================================  
    -- Author: <Author,Sibeesh Venu>  
    -- Create date: <Create Date, 18-Feb-2016>  
    -- Description: <Description,To fetch SalesOrderDetail Page Wise>  
    -- =============================================  
    ALTER PROCEDURE [dbo].[usp_Get_SalesOrderDetailPage] @pageOffset int=0 AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from  
    -- interfering with SELECT statements.  
    SET NOCOUNT ON;  
    WITH CTE_Sales(SlNo, SalesOrderID,SalesOrderDetailID,CarrierTrackingNumber,OrderQty,ProductID,UnitPrice,ModifiedDate) AS  
    ( SELECT ROW_NUMBER() over (  
    ORDER BY ModifiedDate DESC) AS SlNo,  
    SalesOrderID,  
    SalesOrderDetailID,  
    CarrierTrackingNumber,  
    OrderQty,  
    ProductID,  
    UnitPrice,  
    ModifiedDate  
    FROM dbo.SalesOrderDetail)  
    SELECT *  
    FROM CTE_Sales  
    WHERE SlNo>=@pageOffset  
    AND SlNo<@pageOffset+10 END  
    --[usp_Get_SalesOrderDetailPage] 4  


Here we are using Common Table Expressions in SQL Server. If you are new to CTE, you can always visit"Common Table Expression Example" for some more information regarding that. It seems the database is ready with the data now. Then we can go back to our view. We will change our view as follows with the custom styles.
    @{  
    ViewBag.Title = "Index";  
    }  
    <h2>Index</h2>  
    <link href="~/Content/angular-material.css" rel="stylesheet" />  
    <style>  
    .virtualRepeatdemoDeferredLoading #vertical-container {  
    padding: 10px;  
    border: 1px solid #ccc;  
    border-radius: 5px;  
    box-shadow: 1px 10px 10px 1px #ccc;  
    background-color: #fff;  
    width: 40%;  
    height: 390px;  
    margin: 20px;  
    }  
    .virtualRepeatdemoDeferredLoading .repeated-item {  
    border-bottom: 1px solid #ddd;  
    box-sizing: border-box;  
    height: 40px;  
    padding: 10px;  
    border: 1px solid #ccc;  
    border-radius: 5px;  
    box-shadow: 1px 10px 10px 1px #ccc;  
    background-color: #fff;  
    width: 90%;  
    height: 120px;  
    margin: 20px;  
    color: #aaa;  
    font-size: 12px;  
    line-height: 20px;  
    }  
    .virtualRepeatdemoDeferredLoading md-content {  
    margin: 16px;  
    }  
    .virtualRepeatdemoDeferredLoading md-virtual-repeat-container {  
    border: solid 1px grey;  
    }  
    .virtualRepeatdemoDeferredLoading .md-virtual-repeat-container .md-virtual-repeat-offsetter div {  
    padding-left: 16px;  
    }  
    #introduction {  
    border-bottom: 1px solid #ddd;  
    box-sizing: border-box;  
    height: 40px;  
    padding: 10px;  
    border: 1px solid #ccc;  
    border-radius: 5px;  
    box-shadow: 1px 10px 10px 1px #ccc;  
    background-color: #fff;  
    width: 98%;  
    height: 70px;  
    color: #aaa;  
    font-size: 12px;  
    line-height: 25px;  
    }  
    </style>  
    <div ng-controller="AppCtrl as ctrl" ng-cloak="" class="virtualRepeatdemoDeferredLoading" ng-app="MyApp">  
    <md-content layout="column">  
    <div id="introduction">  
    <p>  
    Please scroll the Grid to load the data from database. This is a simple demo of deffered or virtual data loading in Angular JS.  
    We created this application MVC with Web API to fetch the data. I hope you enjoyed the demo. Please visit again <img src="http://sibeeshpassion.com/wp-includes/images/smilies/simple-smile.png" alt=":)" class="wp-smiley" style="height: 1em; max-height: 1em;">  
    </p>  
    </div>  
    <md-virtual-repeat-container id="vertical-container">  
    <div md-virtual-repeat="item in ctrl.dynamicItems" md-on-demand="" class="repeated-item" flex="">  
    <div> <b>SlNo:</b> {{item.SlNo}}, <b>SalesOrderID:</b> {{item.SalesOrderID}}</div>  
    <div> <b>SalesOrderDetailID:</b> {{item.SalesOrderDetailID}}, <b>CarrierTrackingNumber:</b> {{item.CarrierTrackingNumber}}</div>  
    <div> <b>OrderQty:</b> {{item.OrderQty}}, <b>ProductID:</b> {{item.ProductID}}</div>  
    <div> <b>UnitPrice:</b> {{item.UnitPrice}}</div>  
    </div>  
    </md-virtual-repeat-container>  
    </md-content>  
    </div>  
    <script src="~/scripts/angular.min.js"></script>  
    <script src="~/scripts/angular-route.min.js"></script>  
    <script src="~/scripts/angular-aria.min.js"></script>  
    <script src="~/scripts/angular-animate.min.js"></script>  
    <script src="~/scripts/angular-messages.min.js"></script>  
    <script src="~/scripts/angular-material.js"></script>  
    <script src="~/scripts/svg-assets-cache.js"></script>  
    <script src="~/scripts/Default/Default.js"></script>   


As you can see from the above code, our AngularJS controller is ng-controller=”AppCtrl as ctrl” and the AngularJS app is ng-app=”MyApp”. We use md-virtual-repeat as a repeater control, so that it can be used to loop through the object item in ctrl.dynamicItems. Now it is time to create our AngularJS scripts. Shall we?

We can create our Angular App and Controller as follows.
    (function () {  
    'use strict';  
    angular  
    .module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])  
    .controller('AppCtrl', function ($http, $timeout) {  
    });  
    })();


Now in the controller we will add a function with some predefined items as follows.
    var DynamicItems = function () {  
    this.loadedPages = {};  
    this.numItems = 0;  
    this.PAGE_SIZE = 10;  
    this.fetchNumItems_();  
    };  


Here loadedPages is the data collection which is keyed by the page number (Our parameter id in the controller). And numItems is the total number of items. PAGE_SIZE is the number of items to be fetched from each requests.

Now we will create a function to calculate the length of the records.
    DynamicItems.prototype.getLength = function () {  
    return this.numItems;  
    };  


This numItems can be set in the below function.
    DynamicItems.prototype.fetchNumItems_ = function () {  
    $timeout(angular.noop, 300).then(angular.bind(this, function () {  
    this.numItems = 1000;  
    }));  
    };  


Here we are setting the numItems as 1000 for demo purposes, you can always get the count from database and assign it here with a $http request as we load the data from database here, you are yet to see that, no worries.

Below is the function to get the item by index.
    DynamicItems.prototype.getItemAtIndex = function (index) {  
    var pageNumber = Math.floor(index / this.PAGE_SIZE);  
    var page = this.loadedPages[pageNumber];  
    if (page)  
     {  
       return page[index % this.PAGE_SIZE];  
     } else if (page !== null)  
     {  
       this.fetchPage_(pageNumber);  
     }  
    };  

Here is the main part to load the data from database using a $http service in AngularJS.
    DynamicItems.prototype.fetchPage_ = function (pageNumber) {  
    this.loadedPages[pageNumber] = null;  
    $timeout(angular.noop, 300).then(angular.bind(this, function () {  
    var thisObj = this;  
    this.loadedPages[pageNumber] = [];  
    var pageOffset = pageNumber * this.PAGE_SIZE;  
    var myData;  
    var url = '';  
    url = 'api/DataAPI/' + pageOffset;  
    $http({  
    method: 'GET',  
    url: url,  
    }).then(function successCallback(response) {  
    // this callback will be called asynchronously  
    // when the response is available  
    myData = JSON.parse(response.data);  
    pushLoadPages(thisObj, myData)  
    }, function errorCallback(response) {  
    console.log('Oops! Something went wrong while fetching the data. Status Code: ' + response.status + ' Status statusText: ' + response.statusText);  
    // called asynchronously if an error occurs  
    // or server returns response with an error status.  
    });  
    }));  
    };   

As you can see call to our Web API ( url = ‘api/DataAPI/’ + pageOffset;) from $http service, the callback functionSuccessCallback will get the data from database as a response. Once we get the response, we will pass the data to a function pushLoadPages to push the data items to the loadedPages. Cool right? Below is the code snippets for that function.
    function pushLoadPages(thisObj, servData)   
    {  
    if (servData != undefined)  
    {  
        for (var i = 0; i < servData.length; i++)  
        {  
            thisObj.loadedPages[pageNumber].push(servData[i]);  
        }  
    }  
    }  


Here is the complete code for our AngularJS. 

AngularJS Complete Code
    (function () {  
    'use strict';  
    angular  
    .module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])  
    .controller('AppCtrl', function ($http, $timeout) {  
    var DynamicItems = function () {  
    this.loadedPages = {};  
    this.numItems = 0;  
    this.PAGE_SIZE = 10;  
    this.fetchNumItems_();  
    };  
    DynamicItems.prototype.getItemAtIndex = function (index) {  
    var pageNumber = Math.floor(index / this.PAGE_SIZE);  
    var page = this.loadedPages[pageNumber];  
    if (page) {  
    return page[index % this.PAGE_SIZE];  
    } else if (page !== null) {  
    this.fetchPage_(pageNumber);  
    }  
    };  
    DynamicItems.prototype.getLength = function () {  
    return this.numItems;  
    };  
    DynamicItems.prototype.fetchPage_ = function (pageNumber) {  
    this.loadedPages[pageNumber] = null;  
    $timeout(angular.noop, 300).then(angular.bind(this, function () {  
    var thisObj = this;  
    this.loadedPages[pageNumber] = [];  
    var pageOffset = pageNumber * this.PAGE_SIZE;  
    var myData;  
    var url = '';  
    url = 'api/DataAPI/' + pageOffset;  
    $http({  
    method: 'GET',  
    url: url,  
    }).then(function successCallback(response) {  
    // this callback will be called asynchronously  
    // when the response is available  
    myData = JSON.parse(response.data);  
    pushLoadPages(thisObj, myData)  
    }, function errorCallback(response) {  
    console.log('Oops! Something went wrong while fetching the data. Status Code: ' + response.status + ' Status statusText: ' + response.statusText);  
    // called asynchronously if an error occurs  
    // or server returns response with an error status.  
    });  
    function pushLoadPages(thisObj, servData) {  
    if (servData != undefined) {  
    for (var i = 0; i < servData.length; i++) {  
    thisObj.loadedPages[pageNumber].push(servData[i]);  
    }  
    }  
    }  
    }));  
    };  
    DynamicItems.prototype.fetchNumItems_ = function () {  
    $timeout(angular.noop, 300).then(angular.bind(this, function () {  
    this.numItems = 1000;  
    }));  
    };  
    this.dynamicItems = new DynamicItems();  
    });  
    })();    



AngularJS Hosting Europe - HostForLIFE :: Building Role-Based Access Control in Angular Apps

clock June 18, 2025 08:14 by author Peter

Securing components and routes according to user roles is crucial in contemporary online applications. A technique known as role-based access control, or RBAC, permits or prohibits users from accessing specific areas of an application according to the responsibilities they have been allocated. This post will show you how to use role-based logic, services, and route guards to construct RBAC in Angular and manage access throughout your application.

What is RBAC?
Role-Based Access Control is a security mechanism that,

  • Assigns users to roles (like Admin, Editor, User).
  • Defines permissions for each role.
  • Restricts or allows access to routes, components, or actions based on the user’s role.

Prerequisites
Before implementing RBAC, ensure,

  • You have authentication set up (e.g., JWT-based login).
  • Roles are retrievable from the backend or the token.
  • Angular project is initialized with routing (@angular/router).
  • Step-by-Step Implementation

1. Define Roles and User Model
// user.model.ts
export interface User {
  username: string;
  token: string;
  roles: string[]; // e.g., ['Admin', 'User']
}


2. Authentication Service with Role Info

// auth.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private currentUser: User | null = null;

  constructor() {
    const userData = localStorage.getItem('user');
    if (userData) {
      this.currentUser = JSON.parse(userData);
    }
  }

  getUser(): User | null {
    return this.currentUser;
  }

  hasRole(role: string): boolean {
    return this.currentUser?.roles.includes(role) ?? false;
  }

  hasAnyRole(roles: string[]): boolean {
    return roles.some(role => this.hasRole(role));
  }
}


3. Create a Role Guard

// role.guard.ts
import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router
} from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class RoleGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot): boolean {
    const expectedRoles = route.data['roles'] as string[];
    if (this.authService.hasAnyRole(expectedRoles)) {
      return true;
    }

    // Redirect to access denied or login
    this.router.navigate(['/access-denied']);
    return false;
  }
}


4. Use the Role Guard in Routes

// app-routing.module.ts
const routes: Routes = [
  {
    path: 'admin',
    component: AdminDashboardComponent,
    canActivate: [RoleGuard],
    data: { roles: ['Admin'] }
  },
  {
    path: 'editor',
    component: EditorComponent,
    canActivate: [RoleGuard],
    data: { roles: ['Editor', 'Admin'] }
  },
  {
    path: 'access-denied',
    component: AccessDeniedComponent
  }
];


5. Show/Hide UI Based on Roles (Optional)

<!-- in component template -->
<div *ngIf="authService.hasRole('Admin')">
  <button>Edit Settings</button>
</div>


Best Practices for Angular RBAC

  • Always secure routes with guards; never rely solely on front-end UI logic.
  • Store roles securely (e.g., in JWT tokens, not just localStorage).
  • Refresh user roles upon login or token refresh.
  • Keep RBAC logic centralized inside services and guards.
  • Pair RBAC with backend authorization to fully protect APIs.

Conclusion
Implementing RBAC in Angular ensures that your application is secure, maintainable, and scalable. Using route guards, role-aware services, and conditional templates, you can easily control who gets access to what. With this setup, you now have a robust way to protect routes and features based on user roles in your Angular apps. 
Happy coding !!



AngularJS Hosting Europe - HostForLIFE.eu :: Angular Lifecycle Hooks: An Understanding

clock June 13, 2025 08:17 by author Peter

Angular components go through a lifecycle from creation to destruction. Understanding this lifecycle is crucial for writing clean, efficient, and bug-free Angular applications. Angular provides lifecycle hooks that allow developers to tap into these key moments, such as initialization, change detection, and destruction. In this article, we'll explore the most commonly used Angular lifecycle hooks with practical examples and real-world use cases.

What Are Lifecycle Hooks?
Lifecycle hooks are special TypeScript methods that Angular calls automatically at specific points in a component's lifecycle.

They are defined in Angular’s core and include:
    ngOnInit()
    ngOnChanges()
    ngDoCheck()
    ngAfterViewInit()
    ngAfterViewChecked()
    ngAfterContentInit()
    ngAfterContentChecked()
    ngOnDestroy()


1. ngOnChanges()

  • Purpose: Called when any data-bound property (@Input()) changes.
  • Timing: Runs before ngOnInit() and whenever an input changes thereafter.
  • Use Case: Useful when a parent component updates an input value, and the child needs to act on it.

Example
@Input() userId: number;

ngOnChanges(changes: SimpleChanges) {
  if (changes.userId) {
    this.fetchUser(changes.userId.currentValue);
  }
}


2. ngOnInit()

  • Purpose: Called once after the component is initialized.
  • Timing: After the first ngOnChanges().
  • Use Case: Initialize component data (like API calls, default values, subscriptions).

Example
ngOnInit() {
  this.loadDashboardData();
}


3. ngDoCheck()

  • Purpose: Called during every change detection cycle.
  • Timing: After ngOnChanges() and ngOnInit(), and on every change thereafter.
  • Use Case: Custom change detection logic (e.g., detecting changes in deep objects or arrays).

Example
ngDoCheck() {
  if (this.previousLength !== this.items.length) {
    this.previousLength = this.items.length;
    this.onListLengthChanged();
  }
}


Note. Use with caution, as it can lead to performance issues.

4. ngAfterContentInit()

  • Purpose: Called once after Angular projects external content into the component.
  • Timing: After content has been initialized via ng-content.
  • Use Case: Manipulating projected content.

Example
ngAfterContentInit() {
  console.log('Content projected!');
}

5. ngAfterContentChecked()

  • Purpose: Called after every check of the projected content.
  • Timing: After every change detection cycle for ng-content.
  • Use Case: Debugging or Verifying Content Changes.

Example
ngAfterContentChecked() {
  console.log('Projected content checked.');
}

6. ngAfterViewInit()

  • Purpose: Called once after the component’s view (and child views) are initialized.
  • Timing: After Angular sets up the DOM, including @ViewChild queries.
  • Use Case: Interacting with DOM elements.

Example
@ViewChild('inputRef') input: ElementRef;

ngAfterViewInit() {
  this.input.nativeElement.focus(); // Safe to access now
}


7. ngAfterViewChecked()
Purpose: Called after every check of the component’s views.
Timing: After Angular updates the view and child views.
Use Case: Responding to changes in the component’s view (use sparingly).

Example
ngAfterViewChecked() {
  console.log('View checked.');
}


8. ngOnDestroy()

  • Purpose: Called just before Angular destroys the component.
  • Timing: Before component removal from the DOM.
  • Use Case: Cleanup logic like unsubscribing Observables, detaching event listeners, and clearing timers.

Example
subscription: Subscription;

ngOnInit() {
  this.subscription = this.dataService.getData().subscribe(...);
}

ngOnDestroy() {
  this.subscription.unsubscribe(); // Prevent memory leaks
}

Hook Triggered When... Common Use
ngOnChanges Input property changes Respond to input changes
ngOnInit The component is initialized Fetch data, set up
ngDoCheck Every change detection cycle Custom change tracking
ngAfterContentInit External content is projected Handle ng-content
ngAfterContentChecked Projected content checked Debug content projection
ngAfterViewInit Component’s view initialized DOM manipulation
ngAfterViewChecked Component’s view checked View debugging
ngOnDestroy The component is about to be destroyed Cleanup

Best Practices

  • Prefer ngOnInit over constructor() for initialization logic.
  • Always clean up in ngOnDestroy() to prevent memory leaks.
  • Use ngDoCheck and ngAfterViewChecked only when necessary due to performance cost.
  • Keep each lifecycle hook focused, and avoid cluttering with unrelated logic.


Conclusion
Understanding and using Angular’s lifecycle hooks gives you deeper control over your component behavior, especially when dealing with asynchronous data, view rendering, or external libraries.

Whether you’re initializing data, responding to changes, or cleaning up resources, lifecycle hooks help ensure your Angular app remains performant and maintainable.

Happy coding!

 



AngularJS Hosting Europe - HostForLIFE :: How to Call JavaScript Function in Angular 18 Component?

clock May 28, 2025 07:24 by author Peter

This walk will teach you how to use the Angular component to run and call JavaScript methods.

How it’s implemented and what is the description?

  • Create Angular project - AnguWalk.
  • Run the application and check that the project was created successfully.
  • Create JS file with function.
  • Adding JS file link/reference to angular.json
  • Component (TS file) coding.
  • Execute the Project
  • Output

Create an Angular Project called “AnguWalk” using the following CLI command.

Command
ng new AnguWalk

Example

Go inside the project folder and open the Visual Studio code.
Command
cd anguwalk
<enter>
code .
<enter>

Example

Note. Visual Studio code will get started only if your system is configured with path and settings.

Create Javascript file
First, create a JS folder inside the SRC folder.
Right-click on SRC and select the option New Folder.
    Create a file called WelcomeMsg.js.

    function HelloMsg(arg) {
        alert("Welcome to Angular Class - " + arg);
    }


Reference JS File in angular.json file
The Angular.json file is located inside the project root folder. Open and update the SCRIPTS array which is inside the builds object.
"scripts": [
    "src/js/WelcomeMsg.js"
]


Open app.component.html and update the code.
Note. Remove the default code of app.component.html.
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';

declare function HelloMsg(arg: any): void;

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  ngOnInit() {
    HelloMsg("Ashish, Suhana");
  }

  title = 'AnguWalk';
}


Now, run the project to check the output.

Command
ng serve --open

Output

You see the javascript HelloMsg function executed and the alert window displayed successfully.

Happy Coding!



AngularJS Hosting Europe - HostForLIFE :: Use AngularJS to Change Style Dynamically in a Variety of Ways

clock May 8, 2025 08:28 by author Peter

This article describes how to use AngularJS to dynamically alter the style in a variety of ways.


Three distinct methods for dynamic style changes are offered by AngularJS. People are constantly looking for ways to dynamically update their style, and AngularJS can help them in three different ways.

Step 1: You must first add an external Angular.js file to your application. You can do this by downloading my source code, visiting the official AngularJS website, or by clicking this link: ANGULARJS.

<head runat="server">
    <title></title>
    <script src="angular.min.js"></script>
</head>


Step 2. Now I will create some simple CSS classes to be applied dynamically, or you can say at runtime.
<style>
    .changeColor {
        color: blue;
    }
    .changeSize {
        font-size: 30px;
    }
    .italic {
        font-style: italic;
    }
</style>


Here I have created three classes, namely changeColor, changeSize, and italic. The first CSS Class is used for changing the color of the text to Blue, the second is for changing the text size and the last one is for making the Text Italic.

Now we have created the classes to be applied, we need to work on the ViewModel or design part of our application where simple coding needs to be done to apply the change in CSS.

First Method
In the first method, objects of the classes are created that are called using the ng-model, let's see this by implementing it in our application as in the following.
<p ng-class="{changeColor: Color, changeSize: Size, italic: Italic}">
    Changes Will be Seen in Me
</p>
<input type="checkbox" ng-model="Color"> Change Color (apply "changeColor" class)<br>
<input type="checkbox" ng-model="Size"> Change Size (apply "changeSize" class)<br>
<input type="checkbox" ng-model="Italic"> Make it Italic (apply "italic" class)


Here I have applied the CSS to a <p> Tag, and classes are applied using the ng-class directive, here in the ng-class you can see that I have created an object of each class such as for changeColor, "Color" is created.

After this I applied the binding using the ng-model, I created three checkboxes and in each checkbox ng-model is applied, each ng-model provides the binding using the objects created in the ng-class.

So, If we check the first checkbox then the color of the text will change to Blue, similarly, if we check the second and third checkboxes then the text size and its style will change.

Let's see the output.

Output
As I run the application and check the checkboxes then this type of output will be seen

Second Method
Now, I will show you the Second Method of applying the CSS dynamically.

In this method, class names will be called to Apply the CSS.
<p ng-class="style">I Will Show Second Method</p>
<input type="text" ng-model="style" placeholder="Type: changeSize changeColor italic">

Here you can see that in the ng-class I have passed the complete style, since I have passed the style in ng-class it will call all the classes that are created in that style.

Then I created a TextBox to which the style is bound using the ng-model, whatever name is provided in the TextBox, the same style will be applied to the text provided through the <p> tag. If the the name provided in the TextBox doesn't match the CSS Class than nothing will happen to the text and nothing will be applied.

Let's see the output.

Output

Third Method
Now, I will show you the Third Method for applying the CSS dynamically.

In this method, CSS classes will be called in the order they were created.
<p ng-class="[style1, style2, style3]">Using Array Syntax</p>
<input ng-model="style1" placeholder="Type: changeSize, changeColor or italic"><br>
<input ng-model="style2" placeholder="Type: changeSize, changeColor or italic"><br>
<input ng-model="style3" placeholder="Type: changeSize, changeColor or italic"><br>


Here you can see that I have applied the CSS to the <p> tag using the ng-class but this time I have called the classes by the order they were created, style1 is automatically bound to the first class, similarly style2 and style3 are bound to the second and third class.

Then I created three Textboxes that are bound to style 1, 2, and 3 separately, whatever CSS name is provided in these textboxes, the same CSS class will be applied to the Text provided in the <p> tag.

Let's see the output.

Output


The complete code of this application is as follows.
<html ng-app xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">
    <title></title>
    <script src="angular.min.js"></script>
    <style>
        .changeColor {
            color: blue;
        }
        .changeSize {
            font-size: 30px;
        }
        .italic {
            font-style: italic;
        }
    </style>
</head>
<body>
    <p ng-class="{changeColor: Color, changeSize: Size, italic: Italic}">Changes Will be Seen in Me</p>
    <input type="checkbox" ng-model="Color"> Change Color (apply "changeColor" class)<br>
    <input type="checkbox" ng-model="Size"> Change Size (apply "changeSize" class)<br>
    <input type="checkbox" ng-model="Italic"> Make it Italic (apply "italic" class)
    <hr>
    <p ng-class="style">I Will Show Second Method</p>
    <input type="text" ng-model="style" placeholder="Type: changeSize changeColor italic">
    <hr>
    <p ng-class="[stle1, stle2, stle3]">I Will Show Third Method</p>
    <input ng-model="stle1" placeholder="Type: changeSize, changeColor or italic"><br>
    <input ng-model="stle2" placeholder="Type: changeSize, changeColor or italic"><br>
    <input ng-model="stle3" placeholder="Type: changeSize, changeColor or italic"><br>
</body>
</html>



AngularJS Hosting Europe - HostForLIFE :: Create Mobile App with Angular Ionic

clock April 17, 2025 10:57 by author Peter

This post will teach us how to use the Ionic framework to construct a mobile application. Using HTML, CSS, and JavaScript, we can create high-quality mobile applications using Ionic, an open-source UI tool that integrates with well-known frameworks like Angular, React, and Vue.

 


Both the iOS and Android platforms are supported.

Pre-requisite
To start with Ionic Framework, the only requirement is a Node & npm environment, Andriod Studio, command line interface, and Visual Studio code as a code editor.

Step 1. Install ionic

The first step is we need to install ionic tooling. Run the below command to install ionic CLI.

  • native-run: Used to run native binaries on devices and simulators/emulators.
  • cordova-res: Used to generate icons and splash screens for the native app.

npm install -g @ionic/cli native-run cordova-res

Step 2. Create an Application.
Run the below command to create an ionic application. We will create a calculator for this article’s explanation. Check out the below screen prints for command execution.

ionic start calculator

Step 3. Write an Application Code
home.page.html
<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Calculator
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Blank</ion-title>
    </ion-toolbar>
  </ion-header>

  <div id="container">
    <div class="jumbotron col-sm-4 p-2 m-0 bg-inverse mx-auto" style="border: 1px solid lightgray; border-radius: 2%;">
      <label style="font-weight: bolder;">Input</label>
      <div class="input-group input-group-sm col-sm-12 m-0 p-0">
        <div class="col-sm-12 form-control text-lg-right" type="text">{{input}}</div>
      </div>
      <label style="font-weight: bolder;">Result</label>
      <div class="input-group input-group-sm col-sm-12 m-0 p-0">
        <div class="form-control text-sm-right" type="text">{{result}}</div>
      </div>
      <div class="col-sm-12 p-1 m-0">
        <button class="btn btn-info col-sm-6" type="button" (click)="allClear()">C</button>
        <button class="btn btn-warning col-sm-3" type="button" (click)="clear()">x</button>
        <button class="btn btn-secondary col-sm-3" type="button" (click)="pressOperator('/')">/</button>
      </div>
      <div class="col-sm-12 p-1 m-0">
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('7')">7</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('8')">8</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('9')">9</button>
        <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('*')">X</button>
      </div>
      <div class="col-sm-12 p-1 m-0">
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('4')">4</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('5')">5</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('6')">6</button>
        <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('-')">-</button>
      </div>
      <div class="col-sm-12 p-1 m-0">
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('1')">1</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('2')">2</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('3')">3</button>
        <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('+')">+</button>
      </div>
      <div class="col-sm-12 p-1 m-0">
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('.')">.</button>
        <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('0')">0</button>
        <button class="btn btn-success col-sm-6 p-1" type="button" (click)="getAnswer()">=</button>
      </div>
    </div>
  </div>
</ion-content>


home.page.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  input: string = '';
  result: string = '';

  clickNum(num: string) {
    // Do Not Allow . more than once
    if (num == ".") {
      if (this.input != "") {
        const lastNum = this.getLastOperand();
        console.log(lastNum.lastIndexOf("."));
        if (lastNum.lastIndexOf(".") >= 0) return;
      }
    }

    // Do Not Allow 0 at beginning.
    // Javascript will throw Octal literals are not allowed in strict mode.
    if (num == "0") {
      if (this.input == "") {
        return;
      }
      const PrevKey = this.input[this.input.length - 1];
      if (PrevKey === '/' || PrevKey === '*' || PrevKey === '-' || PrevKey === '+') {
        return;
      }
    }

    this.input = this.input + num;
    this.calcAnswer();
  }

  getLastOperand() {
    let pos: number;
    console.log(this.input);
    pos = this.input.toString().lastIndexOf("+");
    if (this.input.toString().lastIndexOf("-") > pos) pos = this.input.lastIndexOf("-");
    if (this.input.toString().lastIndexOf("*") > pos) pos = this.input.lastIndexOf("*");
    if (this.input.toString().lastIndexOf("/") > pos) pos = this.input.lastIndexOf("/");
    console.log('Last ' + this.input.substr(pos + 1));
    return this.input.substr(pos + 1);
  }

  pressOperator(op: string) {
    // Do not allow operators more than once
    const lastKey = this.input[this.input.length - 1];
    if (lastKey === '/' || lastKey === '*' || lastKey === '-' || lastKey === '+') {
      return;
    }

    this.input = this.input + op;
    this.calcAnswer();
  }

  clear() {
    if (this.input != "") {
      this.input = this.input.substr(0, this.input.length - 1);
    }
  }

  allClear() {
    this.result = '';
    this.input = '';
  }

  calcAnswer() {
    let formula = this.input;

    let lastKey = formula[formula.length - 1];

    if (lastKey === '.') {
      formula = formula.substr(0, formula.length - 1);
    }

    lastKey = formula[formula.length - 1];

    if (lastKey === '/' || lastKey === '*' || lastKey === '-' || lastKey === '+' || lastKey === '.') {
      formula = formula.substr(0, formula.length - 1);
    }

    console.log("Formula " + formula);
    this.result = eval(formula);
  }

  getAnswer() {
    this.calcAnswer();
    this.input = this.result;
    if (this.input == "0") this.input = "";
  }
}

TypeScript

home.page.scss

#container {
  text-align: center;

  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

#container strong {
  font-size: 20px;
  line-height: 26px;
}

#container p {
  font-size: 16px;
  line-height: 22px;

  color: #8c8c8c;

  margin: 0;
}

#container a {
  text-decoration: none;
}

.col-sm-3 {
  flex: 0 0 auto;
  width: 23%;
  margin: 1%;
}

@media (min-width: 576px) {
  .col-sm-3 {
      flex: 0 0 auto;
      width: 23%;
      margin: 1%;
  }
}

.form-control{
  min-height: 30px;
}


Step 4. Add required packages.
We can add third-party packages as per requirements. Here for this demo, I have used Bootstrap to make the UI smooth. Run the below command to install the Bootstrap.

Move to the application path using the below command.
cd calculator
npm i bootstrap bootstrap-icons
ng add @ng-bootstrap/ng-bootstrap


Import bootstrap to styles.scss file located at the root level

Step 5. Run the Application with Browser.
Run the below command to execute the application in a browser.
ionic serve

Once the application starts running in the browser you can check the folder structure and check the www folder created/generated. The www is the folder where all JavaScript code is generated for the application. Like dist folder in an angular application here it is a www. Check out the below screen print for a sample.

Step 6. Install the Android SDK.
Once all application runs on the browser, we now need to run the same application on mobile devices like iOS or Android. For this article demonstration, I’m using an Android device. So I’ll generate the .apk file.

Now, let’s see how we can generate the APK file.

Let’s first run the below command in the terminal of the application path to generate apk.
ionic capacitor build android

If Android Studio is installed it will show a response like the below screen print, otherwise, you need to install Android Studio.

On My machine, I have already Android Studio installed. But you can get Android Studio from the mentioned path and get installed like the below screen print
https://developer.android.com/studio

Android Studio SDK path. Sometimes it takes time to download all required packages and it throws errors like the below screen print.

You can just close the error popup and close the project. It will start downloading pending packages and once all required packages are installed it will show a screen like below with the SDK path.

Step 7. Build the APK.
Once the Android studio setup is done we can build apk file. To build apk file go to android studio -> build -> Build App Bundle(s) / APK(s) -> Build APK(s)

Once APK generated you can click on locate and see the apk file.

Let’s install the APK file on an Android device.

Let’s install the APK file on an Android device.

Installed calculator application.
Let’s run application on the Android mobile device.

Conclusion
In this article, we have learned about the ionic framework. We can use it to create a mobile application of angular application. It supports a cross-platform, we can create an app for Android or ios from the source of angular application. We can even use the core logic same for web, desktop, and mobile applications like services, and API calls and only generate different UI components as per respective like desktop, mobile, or web.



AngularJS Hosting Europe - HostForLIFE :: ng-repeat Directive In Angularjs

clock April 10, 2025 13:23 by author Peter

Directives, which are really nothing more than an extension of HTML attributes, are notions that AngularJS offers. The directives ng-init, ng-app, and ng-repeat are a few of them. This post will go over the idea of ng-repeat. In essence, the ng-repeat article is used to tie an array or list of data to HTML controls. For instance, we wish to connect a list of employees into an HTML table or div. Then we can use the ng-repeat directive to do this. So let's start by creating an angular controller and add some data in list of Employees. So our controller will look like the following.

var app = angular.module("mainApp", []);
app.controller('ngRepeatController', function ($scope, $http) {
    $scope.EmpList = [];
    $scope.EmpList.push({ Id: 1, Name: 'User A' });
    $scope.EmpList.push({ Id: 2, Name: 'User B' });
    $scope.EmpList.push({ Id: 3, Name: 'User C' });
    $scope.EmpList.push({ Id: 4, Name: 'User D' });
});

Next, we bind the data to a html table, using the ng-repeat directive. For this, we first bind the controller to a div. and then use the ng-repeat directive on table element. So the code will look like the following.
<div ng-app="mainApp" data-ng-controller="ngRepeatController">
    <table border="1" style="width: 200px">
        <thead>
            <tr>
                <th>Id</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            <tr data-ng-repeat="Emp in EmpList">
                <td>{{ Emp.Id }}</td>
                <td>{{ Emp.Name }}</td>
            </tr>
        </tbody>
    </table>
</div>


Here we have created a simple html table and applied the ng-repeat on the tr tag of the table. This acts as a for-each loop on the rows and the rows get generated, for the number of records in the EmpList array. That's it, run the code and see the results.


And we are done...Happy coding!



AngularJS Hosting Europe - HostForLIFE.eu :: Learn HTTP Interceptors in Angular

clock March 27, 2025 07:39 by author Peter

The HttpClient module in Angular has a feature called an HTTP Interceptor that enables developers to examine and modify HTTP requests and answers globally before application logic handles them.  Tasks including adding authorization tokens to headers, logging, resolving failures, and altering requests and answers are all frequently performed by interceptors.

Key Features of HTTP Interceptors

  • Middleware for HTTP Requests and Responses: Interceptors sit between the application and the backend, processing all HTTP requests and responses.
  • Global Scope: Once an interceptor is configured, it applies to all HTTP requests and responses in the application.
  • Chaining: Multiple interceptors can be implemented, and they are executed in the order they are provided.

Benefits of HTTP Interceptors
Following are some of the key benefits of using HTTP Interceptors in Angular.

  • Testability and reusability: Interceptors are easy to test in isolation, allowing you to ensure that each interceptor behaves correctly
  • Centralized code for cross-cutting concerns: HTTP Interceptors allow you to define logic for common tasks, such as authentication, logging, error handling, or adding headers, in a centralized location.
  • Global application-level modifications: Interceptors operate globally, intercepting all HTTP requests and responses made by the Angular application. This means you can apply changes or perform actions consistently across multiple API calls without having to modify each individual request or response manually.
  • Error handling and logging: Interceptors can be utilized to handle errors globally, providing a consistent approach to error reporting and handling throughout the application.
  • Caching and request/response manipulation: HTTP Interceptors can be leveraged to implement caching mechanisms, reducing redundant requests and optimizing the application’s performance.
  • Separation of concerns: By using HTTP Interceptors, you can keep concerns related to data fetching and communication (HTTP) separate from the business logic of your components and services.
  • Security and authentication: Interceptors are commonly used to add authorization headers or authentication tokens to outgoing requests. This ensures that the user’s authentication status is automatically included in API calls without the need to explicitly set headers in every request.
  • Easy integration with third-party libraries: Interceptors can be used to integrate with third-party libraries or APIs seamlessly. For example, you can apply a specific format to API responses that are expected by a charting library or a data visualization tool.

Logging Interceptor

import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpResponse,
} from '@angular/common/http';
import { Observable, tap } from 'rxjs';

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    console.log('Outgoing HTTP request', request);
    return next.handle(request).pipe(
      tap((event: HttpEvent<any>) => {
        console.log('Incoming HTTP response', event);
      })
    );
  }
}


Provide an interceptor in the app module.
import { LoggingInterceptor } from './interceptors/logging.interceptor';

providers: [
  {
    provide: HTTP_INTERCEPTORS,
    useClass: LoggingInterceptor,
    multi: true,
  },
];

Adding Headers to Requests

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class HeadersInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    console.log(request);
    const GUID = 'f4189b26-01af-432c-bcd8-cb4bc7e90980';
    const modifiedRequest = request.clone({
      setHeaders: {
        GUID,
      },
    });
    return next.handle(modifiedRequest);
  }
}


Provide an interceptor in the app module.
import { HeadersInterceptor  } from './interceptors/headers.interceptor'

providers: [
    {
      provide: HTTP_INTERCEPTORS, useClass: HeadersInterceptor, multi: true
    }
  ]

Error Handling Interceptor

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse,
} from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        // Handle the error here
        console.error('error occurred:', error);
        // throw error as per requirement
        return throwError(error);
      })
    );
  }
}

Provide an interceptor in the app module.
import { ErrorInterceptor } from './interceptors/error.interceptor';

  providers: [
    {
      provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true
    }
  ]

Authentication Interceptor
CLI command: ng generate interceptor auth.
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service'; // Service to get the token

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Get the token from the AuthService
    const authToken = this.authService.getToken();

    // Clone the request and add the Authorization header
    const authReq = authToken
      ? req.clone({
          headers: req.headers.set('Authorization', `Bearer ${authToken}`),
        })
      : req;

    return next.handle(authReq);
  }
}


Create the AuthService
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private token: string | null = null;

  // Simulate storing and retrieving the token
  setToken(token: string): void {
    this.token = token;
  }

  getToken(): string | null {
    return this.token;
  }

  clearToken(): void {
    this.token = null;
  }
}


Register the Interceptor

import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true, // Allow multiple interceptors
    },
  ],
})
export class AppModule {}


Handle Token Expiration

import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService, private router: Router) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const authToken = this.authService.getToken();

    const authReq = authToken
      ? req.clone({
          headers: req.headers.set('Authorization', `Bearer ${authToken}`),
        })
      : req;

    return next.handle(authReq).pipe(
      catchError((error) => {
        if (error.status === 401) {
          // Redirect to login page on unauthorized response
          this.authService.clearToken();
          this.router.navigate(['/login']);
        }
        return throwError(error);
      })
    );
  }
}



AngularJS Hosting Europe - HostForLIFE.eu :: Using Angular to Create an Eye Catching Dashboard

clock March 17, 2025 07:29 by author Peter

With this post, we'll use Recharts with an Angular frontend and a Node.js backend to build a dashboard with stunning charts.

To concentrate on the UI for the time being, we will use hardcoded data. Real data can be connected later.

1.1 Install Angular CLI
Make sure you have Node.js installed. Then install Angular CLI globally:
npm install -g @angular/cli

1.2 Create a New Angular Project
ng new dashboard-app
cd dashboard-app

1.3  Create the Dashboard Component
We’ll create a DashboardComponent with hardcoded data for revenue, user activity, and sales. The component uses Chart.js to render three charts: a line chart for revenue, a line chart for user activity, and a bar chart for sales by category.

Here’s the code for dashboard.component.ts:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Chart } from 'chart.js/auto';

interface ChartData {
  month: string;
  revenue: number;
}

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="dashboard-container p-8">
      <h1 class="text-4xl font-bold mb-8">Dashboard</h1>

      <!-- Stats Grid -->
      <div class="stats-grid mb-8">
        <div class="stat-card" *ngFor="let stat of stats">
          <div class="stat-header">
            <span class="stat-title">{{stat.title}}</span>
            <i class="stat-icon" [class]="stat.icon"></i>
          </div>
          <div class="stat-value">{{stat.value}}</div>
        </div>
      </div>

      <!-- Charts -->
      <div class="charts-grid mb-8">
        <div class="chart-container">
          <canvas id="revenueChart"></canvas>
        </div>
        <div class="chart-container">
          <canvas id="userActivityChart"></canvas>
        </div>
      </div>

      <div class="chart-container">
        <canvas id="salesChart"></canvas>
      </div>
    </div>
  `,
  styles: [`
    .dashboard-container {
      max-width: 1400px;
      margin: 0 auto;
    }

    .stats-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
      gap: 1rem;
    }

    .stat-card {
      background: white;
      padding: 1.5rem;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }

    .stat-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 0.5rem;
    }

    .stat-title {
      font-size: 0.875rem;
      color: #64748b;
    }

    .stat-value {
      font-size: 1.5rem;
      font-weight: bold;
      color: #1e293b;
    }

    .charts-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
      gap: 1rem;
    }

    .chart-container {
      background: white;
      padding: 1rem;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
  `]
})
export class DashboardComponent implements OnInit {
  stats = [
    { title: 'Total Revenue', value: '$443,000', icon: 'bi bi-currency-dollar' },
    { title: 'Average Order', value: '$245', icon: 'bi bi-credit-card' },
    { title: 'Total Customers', value: '12.5K', icon: 'bi bi-people' },
    { title: 'Total Orders', value: '8.2K', icon: 'bi bi-cart' }
  ];

  revenueData = {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    datasets: [{
      label: 'Revenue',
      data: [24000, 26000, 32000, 28000, 35000, 42000, 39000, 45000, 48000, 52000, 49000, 55000],
      borderColor: 'rgb(75, 192, 192)',
      tension: 0.1
    }]
  };

  userActivityData = {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    datasets: [
      {
        label: 'Active Users',
        data: [3200, 3800, 4200, 3900, 3600, 2900, 2800],
        borderColor: 'rgb(75, 192, 192)',
        tension: 0.1
      },
      {
        label: 'New Users',
        data: [1400, 1600, 1800, 1500, 1300, 1000, 900],
        borderColor: 'rgb(255, 99, 132)',
        tension: 0.1
      }
    ]
  };

  salesData = {
    labels: ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'],
    datasets: [{
      label: 'Sales by Category',
      data: [42000, 28000, 15000, 22000, 18000],
      backgroundColor: [
        'rgba(255, 99, 132, 0.5)',
        'rgba(54, 162, 235, 0.5)',
        'rgba(255, 206, 86, 0.5)',
        'rgba(75, 192, 192, 0.5)',
        'rgba(153, 102, 255, 0.5)'
      ]
    }]
  };

  constructor() {}

  ngOnInit(): void {
    this.createCharts();
  }

  private createCharts(): void {
    // Revenue Chart
    new Chart('revenueChart', {
      type: 'line',
      data: this.revenueData,
      options: {
        responsive: true,
        maintainAspectRatio: false
      }
    });

    // User Activity Chart
    new Chart('userActivityChart', {
      type: 'line',
      data: this.userActivityData,
      options: {
        responsive: true,
        maintainAspectRatio: false
      }
    });

    // Sales Chart
    new Chart('salesChart', {
      type: 'bar',
      data: this.salesData,
      options: {
        responsive: true,
        maintainAspectRatio: false
      }
    });
  }
}


Code in app.component.ts

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, CommonModule],
  template: `
    <div class="app-container">
      <router-outlet></router-outlet>
    </div>
  `,
  styles: [`
    .app-container {
      min-height: 100vh;
      background-color: #f8f9fa;
      padding: 1rem;
    }
  `]
})
export class AppComponent {
  title = 'dashboard';
}


Code for not-found.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-not-found',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="not-found-container">
      <div class="error-card">
        <div class="error-header">
          <i class="bi bi-exclamation-circle text-red-500"></i>
          <h1>404 Page Not Found</h1>
        </div>
        <p>The page you're looking for doesn't exist.</p>
      </div>
    </div>
  `,
  styles: [`
    .not-found-container {
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: #f8f9fa;
      padding: 1rem;
    }

    .error-card {
      background: white;
      padding: 2rem;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      max-width: 400px;
      width: 100%;
    }

    .error-header {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      margin-bottom: 1rem;
    }

    h1 {
      font-size: 1.5rem;
      font-weight: bold;
      color: #1e293b;
    }

    p {
      color: #64748b;
    }
  `]
})
export class NotFoundComponent {}

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () => import('./dashboard/dashboard.component')
      .then(m => m.DashboardComponent)
  },
  {
    path: '**',
    loadComponent: () => import('./not-found/not-found.component')
      .then(m => m.NotFoundComponent)
  }
];

Server Side code - index.js
import express, { type Request, Response, NextFunction } from "express";
import { registerRoutes } from "./routes";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// Logging middleware
app.use((req, res, next) => {
  const start = Date.now();
  const path = req.path;
  let capturedJsonResponse: Record<string, any> | undefined = undefined;

  const originalResJson = res.json;
  res.json = function (bodyJson, ...args) {
    capturedJsonResponse = bodyJson;
    return originalResJson.apply(res, [bodyJson, ...args]);
  };

  res.on("finish", () => {
    const duration = Date.now() - start;
    if (path.startsWith("/api")) {
      let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
      if (capturedJsonResponse) {
        logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
      }

      if (logLine.length > 80) {
        logLine = logLine.slice(0, 79) + "…";
      }

      console.log(`${new Date().toLocaleTimeString()} [express] ${logLine}`);
    }
  });

  next();
});

(async () => {
  const server = await registerRoutes(app);

  app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
    const status = err.status || err.statusCode || 500;
    const message = err.message || "Internal Server Error";
    res.status(status).json({ message });
    throw err;
  });

  // Serve static files from the Angular build output directory
  const distPath = path.resolve(__dirname, "..", "dist", "public");
  app.use(express.static(distPath));

  // Always return index.html for any non-API routes (Angular routing)
  app.get("*", (_req, res) => {
    res.sendFile(path.join(distPath, "index.html"));
  });

  const port = 5000;
  server.listen({
    port,
    host: "0.0.0.0",
    reusePort: true,
  }, () => {
    console.log(`${new Date().toLocaleTimeString()} [express] serving on port ${port}`);
  });
})();

angular.json file
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "dashboard": {
      "projectType": "application",
      "schematics": {},
      "root": "client",
      "sourceRoot": "client/src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/public",
            "index": "client/src/index.html",
            "main": "client/src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.json",
            "assets": [
              "client/src/favicon.ico",
              "client/src/assets"
            ],
            "styles": [
              "client/src/styles.css",
              "node_modules/bootstrap-icons/font/bootstrap-icons.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "client/src/environments/environment.ts",
                  "with": "client/src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "dashboard:build:production"
            },
            "development": {
              "browserTarget": "dashboard:build:development"
            }
          },
          "defaultConfiguration": "development"
        }
      }
    }
  }
}

This post showed how to use Angular and Node.js to create an eye-catching dashboard. We produced adaptable and interactive charts that make data come to life by utilizing Chart.js. This stack is perfect for creating scalable apps because of Angular's modular design and Node.js's speed. Additional charts, authentication, and the integration of real-time data sources are possible future enhancements. You may design dashboards of expert quality that meet your demands with this base.



AngularJS Hosting Europe - HostForLIFE.eu :: Feature-Rich User Management System in Angular

clock March 12, 2025 08:56 by author Peter

In this comprehensive guide, we'll build a modern User Management System using Angular. This application showcases several advanced features including theme switching, undo/redo functionality, and robust data management.


Table of Contents

Project Overview

  • Architecture and Design
  • Core Features Implementation
  • Services Implementation
  • Component Development
  • Advanced Features
  • Best Practices and Tips

Project Overview
Our User Management System includes the following features.

  • Modern, responsive design with light/dark theme support
  • Interactive data grid with in-place editing
  • Complete CRUD operations with validation
  • Undo/Redo functionality
  • Data export capabilities (Excel, PDF, PNG)
  • Loading placeholders
  • Enhanced user experience with tooltips
  • Form validation with error messages
  • Duplicate entry prevention
  • Confirmation dialogs
  • Local storage persistence
  • Reusable components

Architecture and Design
Project Structure
src/
├── app/
│   ├── components/
│   │   ├── user-grid/
│   │   └── shared/
│   ├── services/
│   │   ├── user.service.ts
│   │   ├── theme.service.ts
│   │   └── data.service.ts
│   └── models/
│       └── user.model.ts

Core Models

// user.model.ts
export interface User {
    id?: number;
    name: string;
    userName: string;
    email: string;
    phone: string;
    website: string;
}
export interface UserState {
    users: User[];
    undoStack: User[][];
    redoStack: User[][];
}


Core Features Implementation
Theme Service: The theme service manages application-wide theme switching.
    @Injectable({
      providedIn: 'root'
    })
    export class ThemeService {
      private isDarkTheme = new BehaviorSubject<boolean>(false);
      isDarkTheme$ = this.isDarkTheme.asObservable();
      constructor() {
        const savedTheme = localStorage.getItem('theme');
        if (savedTheme) {
          this.setDarkTheme(savedTheme === 'dark');
        }
      }
      setDarkTheme(isDark: boolean) {
        this.isDarkTheme.next(isDark);
        localStorage.setItem('theme', isDark ? 'dark' : 'light');
        document.body.classList.toggle('dark-theme', isDark);
      }
    }


User Service: The User Service handles all data operations with undo/redo support.
@Injectable({
  providedIn: 'root'
})
export class UserService {
  private readonly STORAGE_KEY = 'user_data';

  private state: UserState = {
    users: [],
    undoStack: [],
    redoStack: []
  };

  private usersSubject = new BehaviorSubject<User[]>([]);
  users$ = this.usersSubject.asObservable();

  // CRUD Operations with Undo/Redo Support
  addUser(user: User): boolean {
    if (this.isDuplicate(user)) return false;

    this.pushToUndoStack();
    user.id = this.getNextId();
    this.state.users.push(user);
    this.updateState();

    return true;
  }

  // Additional methods for update, delete, undo, redo
}

Data Grid Component: The main grid component implements the user interface.
@Component({
  selector: 'app-user-grid',
  template: `
    <div class="user-grid-container" [class.dark-theme]="isDarkTheme$ | async">
      <div class="header">
        <h2>User Management</h2>
        <div class="actions">
          <app-button (click)="addNewUser()">Add User</app-button>
          <app-button (click)="toggleTheme()">Toggle Theme</app-button>
        </div>
      </div>
      <!-- Grid implementation -->
    </div>
  `
})
export class UserGridComponent implements OnInit {
  users$ = this.userService.getUsers();
  isDarkTheme$ = this.themeService.isDarkTheme$;

  constructor(
    private userService: UserService,
    private themeService: ThemeService
  ) {}
}


Advanced Features
Export Functionality

export class ExportService {
  exportToExcel(data: User[]) {
    const worksheet = XLSX.utils.json_to_sheet(data);
    const workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, worksheet, 'Users');
    XLSX.writeFile(workbook, 'users.xlsx');
  }
  // Similar methods for PDF and PNG export
}


Loading Placeholders
<ng-container *ngIf="loading$ | async; else userGrid">
  <div class="placeholder-grid">
    <div class="placeholder-row" *ngFor="let i of [1, 2, 3, 4, 5]">
      <!-- Placeholder content -->
    </div>
  </div>
</ng-container>

Form Validation
export class UserFormComponent {
  userForm = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(2)]],
    email: ['', [Validators.required, Validators.email]],
    userName: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9_-]*')]],
    // Additional form controls
  });
}

Tips

  • State Management
    • Use BehaviorSubject for reactive state management
    • Implement undo/redo using stack data structures
    • Persist state changes to localStorage
  • Performance Optimization
    • Use OnPush change detection strategy
    • Implement trackBy functions for ngFor loops
    • Lazy load features when possible
  • Error Handling
    • Implement comprehensive error handling
    • Use toast notifications for user feedback
    • Log errors appropriately

This User Management System demonstrates several Angular best practices and advanced features. Key takeaways include.

  • Proper service architecture for state management
  • Implementing complex features like undo/redo
  • Creating reusable components
  • Managing application themes
  • Handling data persistence
  • Form validation and error handling




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