05th June 2024

Implementing Server Polling in Angular Without RxJS

Server-Polling-Without-Rxjs-VS-Online-img

Server polling is a technique where the client periodically sends requests to the server to check for new data or updates. This guide explains how to implement server polling without using the RxJS library, focusing on using native JavaScript features. We will compare this approach with using RxJS in the introduction and then provide a step-by-step guide to set up and implement polling.

1. Introduction to Server Polling

Server polling involves repeatedly sending HTTP requests to a server at regular intervals to check for updates. It is commonly used in scenarios where real-time updates are required but WebSockets or Server-Sent Events (SSE) are not feasible.

Server polling involves repeatedly sending HTTP requests to a server at regular intervals to check for updates. This method is useful when real-time updates are required but WebSockets or Server-Sent Events (SSE) are not feasible.

Approaches to Implement Server Polling
With RxJS:
  • RxJS (Reactive Extensions for JavaScript) provides a powerful and flexible way to handle asynchronous data streams.
  • Polling with RxJS involves using observables and operators like interval, switchMap, and retry to manage the polling logic.
  • This approach is highly declarative, making it easy to compose and manage complex polling scenarios.
Without RxJS:
  • Native JavaScript features like setInterval and the Fetch API can be used to implement polling.
  • This approach is more straightforward and does not require an additional library.
  • It is suitable for simpler polling requirements and offers better control over the polling logic.

In this guide, we will focus on the second approach, implementing server polling using native JavaScript features within an Angular application.

2. Setting Up a Simple Server

We'll use Node.js and Express to set up a simple server that responds with some data.

Step-by-Step Server Setup
  • Initialize a new Node.js project:
                                
                                    
    mkdir server-polling
    cd server-polling
    npm init -y
  
                                
                            
  • Install Express and CORS: Install Express, a fast, unopinionated, minimalist web framework for Node.js, and CORS middleware to handle Cross-Origin Resource Sharing:
                                
                                    
    npm install express cors
  
                                
                            
  • Create a server file: Create a file named server.js and add the following code:
                                
                                    
    const express = require('express');
    const cors = require('cors'); // Import the cors package
    const app = express();
    const port = 3000;

    app.use(cors()); // Use the cors middleware

    app.get('/data', (req, res) => {
      const data = {
        timestamp: new Date(),
        message: 'Hello, this is your data!',
      };
      res.json(data);
    });

    app.listen(port, () => {
      console.log('Server is running on http://localhost:port');
    });
  
                                
                            
  • Run the server:
                                
                                    
    node server.js
  
                                
                            

Your server should now be running on http://localhost:3000 and respond with JSON data when you access http://localhost:3000/data.

3. Creating an Angular Project

We'll create a new Angular project to implement client-side polling.

Step-by-Step Angular Project Setup
  • Install Angular CLI: If you haven't installed the Angular CLI, you can do so with the following command:
                                
                                    
    npm install -g @angular/cli
  
                                
                            
  • Create a new Angular project:
                                
                                    
    ng new angular-polling
    cd angular-polling
 
                                
                            
  • Serve the Angular project:
                                
                                    
    ng serve
 
                                
                            

Your Angular project should now be running on http://localhost:4200.

4. Implementing Client-Side Polling in Angular

Step-by-Step Polling Implementation
  • Generate a service:
                                
                                    
    ng generate service polling
 
                                
                            
  • Update the Polling Service: Modify polling.service.ts to include the polling logic using setInterval and the Fetch API.
                                
                                    
  import { Injectable } from '@angular/core';

    @Injectable({
      providedIn: 'root'
    })
    export class PollingService {
      private pollInterval = 5000; // Poll every 5 seconds

      constructor() { }

      fetchData(): void {
        setInterval(() => {
          fetch('http://localhost:3000/data')
            .then(response => {
              if (!response.ok) {
                throw new Error('HTTP error! Status: response.status');
              }
              return response.json();
            })
            .then(data => {
              console.log('Fetched data:', data);
            })
            .catch(error => {
              console.error('Error fetching data:', error);
            });
        }, this.pollInterval);
      }
    }
 
                                
                            
  • Inject and Use the Polling Service: Modify app.component.ts to use the PollingService.
                                
                                    
  import { Component, OnInit } from '@angular/core';
  import { PollingService } from './polling.service';

  @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
  })
  export class AppComponent implements OnInit {
    constructor(private pollingService: PollingService) {}

    ngOnInit(): void {
      this.pollingService.fetchData();
    }
  }
 
                                
                            
  • Update the App Component Template: Modify app.component.html to display the fetched data.
                                
                                    
  <h1>Server Polling Example</h1>
    <div id="data">
      <p *ngIf="!data">Loading...</p>
      <p *ngIf="data">Timestamp: {{ data.timestamp | date:'medium' }}</p>
      <p *ngIf="data">Message: {{ data.message }}</p>
    </div>
 
                                
                            
  • Update the Polling Service to Store Data: Modify polling.service.ts to store the fetched data and make it available to the component.
                                
                                    
  import { Injectable } from '@angular/core';
  import { BehaviorSubject } from 'rxjs';

  @Injectable({
    providedIn: 'root'
  })
  export class PollingService {
    private pollInterval = 5000; // Poll every 5 seconds
    private dataSubject = new BehaviorSubject<any>(null);
    public data$ = this.dataSubject.asObservable();

    constructor() { }

    fetchData(): void {
      setInterval(() => {
        fetch('http://localhost:3000/data')
          .then(response => {
            if (!response.ok) {
              throw new Error('HTTP error! Status: response.status');
            }
            return response.json();
          })
          .then(data => {
            this.dataSubject.next(data);
          })
          .catch(error => {
            console.error('Error fetching data:', error);
          });
      }, this.pollInterval);
    }
  }
 
                                
                            
  • Update the App Component to Subscribe to Data: Modify app.component.ts to subscribe to the data from the service.
                                
                                    
  import { Component, OnInit } from '@angular/core';
  import { PollingService } from './polling.service';

  @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
  })
  export class AppComponent implements OnInit {
    data: any;

    constructor(private pollingService: PollingService) {}

    ngOnInit(): void {
      this.pollingService.data$.subscribe(data => {
        this.data = data;
      });

      this.pollingService.fetchData();
    }
  }
 
                                
                            

5. Handling Server Responses and Errors

Proper error handling is crucial for any application. In our polling service, we are already catching errors and logging them to the console. However, you may want to handle errors more gracefully, such as displaying error messages to the user.

Enhanced Error Handling
  • Update the Polling Service: Modify polling.service.ts to store error messages and make them available to the component.
                                
                                    
  import { Injectable } from '@angular/core';
  import { BehaviorSubject } from 'rxjs';

  @Injectable({
    providedIn: 'root'
  })
  export class PollingService {
    private pollInterval = 5000; // Poll every 5 seconds
    private dataSubject = new BehaviorSubject<any>(null);
    private errorSubject = new BehaviorSubject<string | null>(null);
    public data$ = this.dataSubject.asObservable();
    public error$ = this.errorSubject.asObservable();

    constructor() { }

    fetchData(): void {
      setInterval(() => {
        fetch('http://localhost:3000/data')
          .then(response => {
            if (!response.ok) {
              throw new Error('HTTP error! Status: response.status');
            }
            return response.json();
          })
          .then(data => {
            this.dataSubject.next(data);
            this.errorSubject.next(null); // Clear any previous errors
          })
          .catch(error => {
            console.error('Error fetching data:', error);
            this.errorSubject.next(error.message);
          });
      }, this.pollInterval);
    }
  }
 
                                
                            
  • Update the App Component to Display Errors: Modify app.component.ts to subscribe to the error messages.
                                
                                    
  import { Component, OnInit } from '@angular/core';
  import { PollingService } from './polling.service';

  @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
  })
  export class AppComponent implements OnInit {
    data: any;
    error: string | null = null;

    constructor(private pollingService: PollingService) {}

    ngOnInit(): void {
      this.pollingService.data$.subscribe(data => {
        this.data = data;
      });

      this.pollingService.error$.subscribe(error => {
        this.error = error;
      });

      this.pollingService.fetchData();
    }
  }
 
                                
                            
  • Update the App Component Template: Modify app.component.html to display error messages.
                                
                                    
  <h1>Server Polling Example</h1>
  <div id="data">
    <p *ngIf="!data && !error">Loading...</p>
    <p *ngIf="error">Error: {{ error }}</p>
    <p *ngIf="data">Timestamp: {{ data.timestamp | date:'medium' }}</p>
    <p *ngIf="data">Message: {{ data.message }}</p>
  </div>
 
                                
                            

6. Conclusion

In this guide, we've walked through implementing server polling in an Angular application without using the RxJS library. By leveraging native JavaScript features like fetch and setInterval, we can achieve a similar result with more control and simplicity. This approach is suitable for applications with straightforward polling requirements, providing a lightweight and efficient solution for keeping client data up to date.

Feel free to experiment with this approach and customize it according to your needs. Happy coding!

Let's develop your ideas into reality