React vs Angular

React Initialization-
most devs use Create React App (CRA) to set up project architecture and necessary configurations.
npx create-react-app my-app
Angular Initialization-
For standard Angular app, with NgModule.
ng new my-app  --no-standalone
For standalone app, with no extra NgModule file.
ng new my-app  
React-
1. Entry Point-

Entry point of React app is `index.html` in `public` directory. This file contains a `< div>` element with an id where the React app will be mounted.

index.html
<!DOCTYPE html> 
<html lang="en">
  <head>
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    
  </body>
</html>
2. Main Entry Point

The index.js (or index.tsx for TypeScript) file in the src directory is the main entry point of the React application. This file contains the code to render the root component of the React app into the DOM.

index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';

import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)
3. Rendering the App

In the index.js file, React’s ReactDOM.render or the new createRoot API is used to render the root component (usually App).

4. Linking

The index.js file is responsible for rendering the React components into the HTML element with the id of root. But index.html doesn't have a link to the index.js. So how does the index.js knows which root element it is referring to?

This is taken care of during building and linking


  • During the build process, Webpack (or the equivalent bundler) compiles the JavaScript files, including index.js, into one or more bundles.

  • These bundles are then automatically injected into the index.html file. Create React App uses Webpack to manage this injection.

5. Serving The App

  • When you run the development server (npm start or yarn start), Webpack Dev Server serves the index.html file, with the script tags pointing to the bundled JavaScript files.

  • In production builds (npm run build), Webpack generates the index.html with script tags linking to the minified and optimized JavaScript bundles.

  • By abstracting away the manual linking through the build tools, developers can focus on writing React components and application logic without worrying about how the JavaScript files are linked to the HTML file. This setup ensures that the index.js file knows to render the application into the root element.

Angular-
1. Key Files-

  • src/index.html: The main HTML file. Angular will inject the application into the <app-root> </app-root> element.
  • src/main.ts: The main entry point for the Angular application. This file bootstraps the root module (AppModule).
  • src/app/app.module.ts: The root module of the application, declaring and importing components and other modules.
  • src/app/app.component.ts: The root component of the application.

2. Entry Point-

main.ts: This is the entry point of the application. It uses the platformBrowserDynamic().bootstrapModule(AppModule) function to bootstrap the root module.

main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
3. Other Files-

AppModule (app.module.ts): This is the root module that declares the root component and imports other modules.

app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
4. How Angular Bootstraps

a. First it loads the index.html file.
b. We have <app-root> in body of the html.
c. To run our ang app, we use ng serve. It builds our app, and saves it to the memory (not disk) and starts the development server. Also watches over the project.
d. Ng serve compiles our app, generates some bundle and injects it into index.html. To see those bundles, we need to ng build.We'll see it as folder dist.
e. We have 3 scripts in index.html after build - runtime.js( webpack runtime file ), polyfills.js (for supporting all the latest browsers and older browsers), main.js( code of app , converted from TS to JS).
f. Even the styles.css that we see in index.html post-build, if we open page source on browsers, we see bundled as a js file and added to the body of index.html as a js file.
g. Ang CLI uses webpack to traverse through our Ang app and it bundles JS and other files into bundles.
h. When index.html is loaded, Ang core libraries and 3rd party libraries are also loaded by that time.
i. Now Ang needs to locate the main entry point , searches angular.json file to look for main (main.ts).
j. In main.ts we have platformBrowserDynamic , it is a module responsible for loading the Ang app in desktop browsers.
k. Ang apps are organized as modules.
l. The module that is loaded first is called as Root Module. In v16 , root module is AppModule - app.module.ts .
m. In v16, in AppModule we have bootstrap that specifies what component should be loaded when AppModule is loaded. By default it is AppComponent.
n. Now we'll go to AppComponent. It'll load the app.component.html .
o. Wherever we use the selector (app-root) , Ang will render the view template (app.component.html) in its place.
p.Ang Project→index.html→angular.json→main.ts→AppModule→AppComponent→View Template (app.component.html).

React Component

1. We create a file in "components" folder , named as "Header.js" and use 'rfc' to create a basic component template.

Header.js
import React from 'react'

export default function header() {
  return (
    <div>header</div>
  )
}











2. Add jsx to component file. Add css file as either as component.css or add the styles to App.css .

Header.js
import React from "react";
import "./Header.css";

const Header = () => {
  return (
    <header className="header">
      <div className="logo">Ecom-React</div>
      <div className="search">
        <input type="text" placeholder="Search..." />
      </div>
      <div className="actions">
        <div className="user">User</div>
        <div className="sign-in">Sign In</div>
        <div className="cart">Cart</div>
      </div>
    </header>
  );
};
  export default Header;
      

  • Add the <Header/> component to App.js to have it rendered on the web.
  • App.js
    import React from "react";
    import Header from "./components/Header";
    
    function App() {
      return <div>
        <Header/>
      </div>;
    }
    
    export default App;
    
    Angular Component

    1. we use " ng g c header " to create a component.We get 4 files - header.component.ts , header.component.html , header.component.css , header.component.spec.ts .

    header.component.html
    <p>header works!</p>
    header.component.ts
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-header',
      templateUrl: './header.component.html',
      styleUrl: './header.component.scss'
    })
    export class HeaderComponent {
    
    }
    

    2. We add our template html code to header.component.html .

    header.component.html
    <header class="header">
      <div class="logo">Logo</div>
      <div class="search">
        <input type="text" placeholder="Search..." />
      </div>
      <div class="actions">
        <div class="user">User</div>
        <div class="sign-in">Sign In</div>
        <div class="cart">Cart</div>
      </div>
    </header>
    

  • To use the header component, include it in your main application template. Open the app.component.html file and add the <app-header> tag, to have it rendered on the web.
  • app.component.html
    <app-header></app-header>
    <!-- Other components go here -->
  • Add HeaderComponent to declarations in app.module.ts
  • import { NgModule } from '@angular/core';
    import { BrowserModule, provideClientHydration } from '@angular/platform-browser';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    
    import { HeaderComponent } from './components/header/header.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        HeaderComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule
      ],
      providers: [
        provideClientHydration()
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
    React

    1. Generally fetching of data is done in the same component that renders the UI on web.
    For example if we want to fetch products from backend. We can use

  • axios/fetch to fetch the data from server,
  • useState to set state of the products, so React can track that state during changes.
  • useEffect to perform side effects like fetching data, directly updating the DOM, timers.
  • components/Products.js
    import React, {useState,useEffect} from 'react';
    import "./Products.css";
    import axios from 'axios';
    
    export default function Products() {
      const [products,setProducts]=useState([]);
      const [loading,setLoading]=useState(true);
      const [error,setError]=useState(null);
    
      useEffect(()=>{
        const fetchProducts=async()=>{
          try {
            const response = await axios.get("http://localhost:3001/products");
            setProducts(response.data);
            setLoading(false);
          } catch (error) {
            setError(error);
            console.log("error in Products while fetching products - "+error);
            setLoading(false);
          }
        }
        fetchProducts();
      })
    
      if(loading) return <p>Loading.....</p>;
      if(error) return <p>Error: {error.message}</p>
      return (
        <div class="product-list">
          {
            products.map(product=>(
              <div key={product.id} className="product-item">
                <h2>{product.name}</h2>
                <p>{product.description}</p>
                <p>Rs. {product.price}</p>
                <button>Add to cart</button>
              </div>
            ))
          }
        </div>
      )
    }

    We can then import this component straight in App.js .

    Angular

    1.
    1.1 We generate a product component.
    1.2 Set Up a service to fetch data. (axios statement in react)
    1.3 Call the service inside product.component.ts . (like fetchProducts() in useEffect).
    1.3 Loop the product in product.component.html. (like products.map())

    Steps-
    2.

  • Generate new component using "ng g c components/product".
  • Set up a service to fetch data. Use "ng g service services/product" .
  • Edit `product.service.ts` to include the logic for fetching data.

    product.service.ts
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class ProductService {
    
      private apiUrl = 'YOUR_API_URL'; // Replace with your actual API URL
    
      constructor(private http: HttpClient) { }
    
      getProducts(): Observable {
        return this.http.get(this.apiUrl);
      }
    }
    

  • Injectable decorator allows Angular to inject dependencies into the service.
  • HttpClient to perform HTTP operations.
  • Observable represents a stream of data that can be observed, allowing you to handle async data.
  • The @Injectable decorator marks the class as a service that can be injected. The { providedIn: 'root' } configuration makes this service available application-wide, so it doesn't need to be provided in a specific module.
  • constructor(private http: HttpClient) { }: This is a dependency injection. Angular automatically provides an instance of HttpClient when the ProductService is created, allowing the service to make HTTP requests.
  • Angular will automatically provide an instance of this service where it is needed. You can subscribe to the Observable returned by this method to get the product data.
  • 3.

  • Update the Product Component
    Edit product.component.ts to fetch and display the products:
  • product.component.ts
    import { Component, OnInit } from '@angular/core';
    import { ProductService } from '../product.service';
    
    @Component({
      selector: 'app-product',
      templateUrl: './product.component.html',
      styleUrls: ['./product.component.css']
    })
    export class ProductComponent implements OnInit {
      products: any[] = [];
      loading = true;
      error: string | null = null;
    
      constructor(private productService: ProductService) { }
    
      ngOnInit(): void {
        this.productService.getProducts().subscribe({
          next: (data) => {
            this.products = data;
            this.loading = false;
          },
          error: (err) => {
            this.error = err.message;
            this.loading = false;
          }
        });
      }
    }
    
  • OnInit: Import the OnInit lifecycle hook interface to perform initialization logic in the ngOnInit method.
  • constructor(private productService: ProductService): Injects the ProductService into the component, allowing it to call the service's methods.
  • ngOnInit(): Lifecycle hook that is called after the component is initialized.
  • this.productService.getProducts().subscribe(...): Calls the getProducts method from the ProductService, which returns an Observable.
  • .subscribe(...): Subscribes to the Observable to handle the data or error when the HTTP request completes.
  • (data) => { ... }: Arrow function to handle successful data retrieval. Assigns the received data to this.products and sets loading to false.
  • (err) => { ... }: Arrow function to handle errors. Assigns the error message to this.error and sets loading to false.
  • 4.

  • Update the Product Component Template
    Edit product.component.html to display the products:
  • product.component.html
    <div *ngIf="loading">Loading...</div>
    <div *ngIf="error">{{ error }}</div>
    <div *ngIf="!loading && !error" class="product-list">
      <div *ngFor="let product of products" class="product-item">
        <h2>{{ product.name }}</h2>
        <p>{{ product.description }}</p>
        <p>\${{ product.price }}</p>
      </div>
    </div>
    

    5.

  • Ensure Proper Module Imports
    Make sure that the HttpClientModule is imported in your AppModule. Open app.module.ts and ensure it includes HttpClientModule:
  • app.module.ts
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { HttpClientModule } from '@angular/common/http';
    
    import { AppComponent } from './app.component';
    import { ProductComponent } from './product/product.component';
    import { ProductService } from './product.service';
    
    @NgModule({
      declarations: [
        AppComponent,
        ProductComponent
      ],
      imports: [
        BrowserModule,
        HttpClientModule
      ],
      providers: [ProductService],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    6.

  • Integrate the Product Component To use the product component, include it in your main application template. Open app.component.html and add the <app-product> tag:
  • app.component.html
    <app-product></app-product>
    <!-- Other components go here -->
    

    • Why can't we just define a function in product.component.ts to fetch the products, like in React, and use that in product.component.html, instead of creating a whole another service?

    While you technically can define a function within the product.component.ts to fetch the products, using a service offers several advantages:
    Separation of Concerns: By placing the data-fetching logic in a service, you separate business logic from the UI logic, making your codebase cleaner and more maintainable.
    Reusability: Services can be reused across different components, avoiding code duplication.
    Testability: Services are easier to unit test independently of the components that use them.
    Single Responsibility Principle: Components should focus on presenting data and handling user interactions, while services should handle data retrieval and manipulation.



    Can we use Promises instead of Observables?

    product.service.ts
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    @Injectable({
      providedIn: 'root'
    })
    export class ProductService {
      private apiUrl = 'YOUR_API_URL'; // Replace with your actual API URL
    
      constructor(private http: HttpClient) { }
    
      getProducts(): Promise {
        return this.http.get(this.apiUrl).toPromise();
      }
    }
    
    product.component.ts
    import { Component, OnInit } from '@angular/core';
    import { ProductService } from '../product.service';
    
    @Component({
      selector: 'app-product',
      templateUrl: './product.component.html',
      styleUrls: ['./product.component.css']
    })
    export class ProductComponent implements OnInit {
      products: any[] = [];
      loading = true;
      error: string | null = null;
    
      constructor(private productService: ProductService) { }
    
      async ngOnInit(): Promise {
        try {
          this.products = await this.productService.getProducts();
          this.loading = false;
        } catch (err) {
          this.error = err.message;
          this.loading = false;
        }
      }
    }
    

    But Promises are way less powerful than Observables.



    Can we use axios directly instead of services? We can, but it goes against Angular best practices of separating concerns and reusability.

    import { Component, OnInit } from '@angular/core';
    import axios from 'axios';
    
    @Component({
      selector: 'app-product',
      templateUrl: './product.component.html',
      styleUrls: ['./product.component.css']
    })
    export class ProductComponent implements OnInit {
      products = [];
      loading = true;
      error = null;
    
      ngOnInit(): void {
        axios.get('YOUR_API_URL')
          .then(response => {
            this.products = response.data;
            this.loading = false;
          })
          .catch(error => {
            this.error = error.message;
            this.loading = false;
          });
      }
    }
    
    Disadvantages:

  • Separation of Concerns: Mixing HTTP logic with UI logic makes the component harder to maintain and test.
  • Reusability: Service logic can be reused across multiple components, but this method cannot.
  • Testing: Unit testing becomes more difficult as you cannot easily mock Axios calls.
  • React

    There's just 3 steps required to Route a specific component to a particular route.-

  • install react-router-dom and create component
  • Add route to App.js
  • Link the button/link to that route.

  • 1. install react-router-dom

    npm i react-router-dom

    Create the component in component folder , add desired css.

    component/Login.js
    import axios from 'axios'
    import React from 'react'
    import './Login.css'
    
    export default function Login() {
      return (
        <div className="login-form">
          <h2>Login</h2>
          <form>
            <div className="form-group">
              <label>Email</label>
              <input type="email" id="email" required />
            </div>
    
            <div className="form-group">
              <label>Password</label>
              <input type="password" id="password" required />
            </div>
    
            <button type="submit">Login</button>
          </form>
        </div>
      );
    }
    

    2.Add the desired path to App.js

    App.js
    import React from "react";
    import Header from "./components/Header";
    import Products from "./components/Products";
    import Login from "./components/Login";
    
    import { BrowserRouter,Routes, Route } from "react-router-dom";
    
    function App() {
      return <BrowserRouter>
        <Header/>
          <Routes>
            <Route path="/" element={<Products/> }/>
            <Route path="/login" element={<Login/> }/>
          </Routes>
      </BrowserRouter>;
    }
    
    export default App;
    












    3. Link whatever button/link redirects to this route/component.
    Suppose the button "Sign-In" in Header.js leads to this route.
    Use <Link to="/route"> and not <a href="/route" >.

    Header.js
    import React from "react";
    import "./Header.css";
    import { Link } from "react-router-dom";
    
    const Header = () => {
      return (
        <header className="header">
          <div className="logo">Ecom-React</div>
          <div className="search">
            <input type="text" placeholder="Search..." />
          </div>
          <div className="actions">
            <div className="user">User</div>
            <div className="sign-in">
              <Link to="/login">Sign In</Link>
            </div>
            <div className="cart">Cart</div>
          </div>
        </header>
      );
    };
    
    export default Header;
    

    Angular

  • Generate the desired component, example login in this case.
  • Add routes to app-routing.module.ts .
  • Modify the button/link that redirects to the route.


  • 1. Create the login component.
  • ng g c components/login
    login.component.html
    <div class="login-form">
        <h2>Login</h2>
        <form >
            <div class="form-group">
                <label for="email">Email</label>
                <input type="email" id="email" required>
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" required>
            </div>
            <button type="submit">Login</button>
        </form>
    </div>










    2. Configure your app's routes to include the login route. Update the routing module (usually app-routing.module.ts).

    app-routing.module.ts
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { ProductComponent } from './components/product/product.component';
    import { LoginComponent } from './components/login/login.component';
    
    const routes: Routes = [
      {path:'login',component:LoginComponent},
      {path:'products',component:ProductComponent},
      {path:'',redirectTo:'/products',pathMatch:'full'}
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
  • Remove the components from app.component.html that are linked to some route, and add <router-outlet></router-outlet> instead. Keep only the components that are permanent.
  • <app-header></app-header>
    <router-outlet></router-outlet>

    3. Link the button/link to the route.

    header.component.ts
    import { Component } from '@angular/core';
    import {Router} from '@angular/router';
    
    @Component({
      selector: 'app-header',
      templateUrl: './header.component.html',
      styleUrl: './header.component.scss'
    })
    export class HeaderComponent {
      constructor(private router:Router){}
    
      navigateToLogin():void{
        this.router.navigate(['/login'])
      }
    }
    
    header.component.html
    <header class="header">
        <div class="logo">Ecom-Angular</div>
        <div class="search">
            <input type="text" placeholder="Search.......">
        </div>
        <div class="actions">
            <div class="user">User</div>
            <div class="sign-in" (click)="navigateToLogin()">Login</div>
            <div class="cart">Cart</div>
        </div>
    </header>
                  

    React

    1. React, we do the data sending, and the UI part in one component.

    import React, { useState } from "react";
    import "./Register.css";
    import axios from "axios";
    
    const Register = () => {
      const [formData, setFormData] = useState({
        name: "",
        password: "",
        email: "",
        address: "",
        role: "user",
        products: [], 
        createdAt: new Date().toISOString()
      });
    
      const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData({
          ...formData,
          [name]: value
        });
      };
    
      const handleSubmit = async (e) => {
        e.preventDefault();
    
        try {
          const response = await axios.post(
            "http://localhost:3001/users",
            formData
          );
          console.log("User registered:", response.data);
        } catch (error) {
          console.error("Error regarding user:", error);
        }
      };
      return (
        <div className="register-container">
          <h2>Register</h2>
          <form onSubmit={handleSubmit}>
            <label htmlFor="name">Name:</label>
            <input
              type="text"
              id="name"
              name="name"
              value={formData.name}
              onChange={handleChange}
              required
            />
    
            <label htmlFor="password">Password:</label>
            <input
              type="password"
              id="password"
              name="password"
              value={formData.password}
              onChange={handleChange}
              required
            />
    
            <label htmlFor="email">Email:</label>
            <input
              type="email"
              id="email"
              name="email"
              value={formData.email}
              onChange={handleChange}
              required
            />
    
            <label htmlFor="address">Address:</label>
            <textarea
              id="address"
              name="address"
              value={formData.address}
              onChange={handleChange}
              required
            />
    
            <button type="submit">Register</button>
          </form>
        </div>
      );
    };
    
    export default Register;

    2. If we're sending the data to json server, we'll put the data that we DO NOT get from user, but is required by the schema , in the formData itself.

    const [formData, setFormData] = useState({
        name: "",
        password: "",
        email: "",
        address: "",
        role: "user",
        products: [], 
        createdAt: new Date().toISOString()
      });

    like products ,role and createdAt are sent in formData itself.

    3. If we're using a backend like nodeJs or SpringBoot, we'll handle this logic in the backend controllers itself, instead of sending them through React directly.

    Angular

    There are 3 steps to follow-

  • Create a Service for HTTP Requests
  • Create a Register Component
  • Handle Form Submission
  • 1. Create a Service for HTTP Requests

    ng generate service user
    services/user.service.ts
    
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class UserService {
      private apiUrl = 'http://localhost:3001/users';
    
      constructor(private http: HttpClient) {}
    
      registerUser(userData: any): Observable {
        return this.http.post(this.apiUrl, userData);
      }
    }
    

    2. Create a Register Component and add it to app.module.ts .

    ng g c register
    components/register.component.ts
    
    import { Component } from '@angular/core';
    import { FormBuilder, FormGroup, Validators } from '@angular/forms';
    import { UserService } from '../user.service';
    
    @Component({
      selector: 'app-register',
      templateUrl: './register.component.html',
      styleUrls: ['./register.component.css']
    })
    export class RegisterComponent {
      registerForm: FormGroup;
    
      constructor(private fb: FormBuilder, private userService: UserService) {
        this.registerForm = this.fb.group({
          name: ['', Validators.required],
          password: ['', Validators.required],
          email: ['', [Validators.required, Validators.email]],
          address: ['', Validators.required]
        });
      }
    
      onSubmit() {
        if (this.registerForm.valid) {
          const formData = {
            ...this.registerForm.value,
            role: 'user',
            products: [],
            createdAt: new Date().toISOString()
          };
    
          this.userService.registerUser(formData).subscribe(
            (response) => {
              console.log('User registered:', response);
            },
            (error) => {
              console.error('Error registering user:', error);
            }
          );
        }
      }
    }
    

    FormBuilder, FormGroup, Validators: These are used for creating and managing reactive forms.

    FormGroup: is a class in Angular used to group multiple form controls (like input fields) together. Think of it as a container for form controls. Each form control inside the FormGroup is represented by an instance of FormControl.

    Group Form Controls: FormGroup is used to organize multiple form controls (fields) into a single form.
    Track State and Value: It tracks the value and validity state of the form controls inside it.
    Manual Setup: You manually set up each form control inside the FormGroup.

    import { FormGroup, FormControl, Validators } from '@angular/forms';
    
    const registerForm = new FormGroup({
      name: new FormControl('', Validators.required),  // Add name field
      password: new FormControl('', Validators.required), // Add password field
      email: new FormControl('', [Validators.required, Validators.email]), // Add email field
      address: new FormControl('', Validators.required) // Add address field
    });
    

    FormBuilder: is a service provided by Angular to help create FormGroup and FormControl instances in a more streamlined and less verbose way. It provides methods to quickly set up your form structure.
    Key Points about FormBuilder:
    Simplifies Form Creation: FormBuilder reduces the boilerplate code required to set up forms.
    Convenient Methods: It has methods like group() to create a FormGroup and control() to create a FormControl.

    import { FormBuilder, Validators } from '@angular/forms';
    
    constructor(private fb: FormBuilder) {
      this.registerForm = this.fb.group({
        name: ['', Validators.required],
        password: ['', Validators.required],
        email: ['', [Validators.required, Validators.email]],
        address: ['', Validators.required]
      });
    }
    

    3. Create a template and add styles.

    register.component.html
    
    
    <div class="register-container">
      <h2>Register</h2>
      <form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
        <label for="name">Name:</label>
        <input id="name" formControlName="name" type="text" required />
    
        <label for="password">Password:</label>
        <input id="password" formControlName="password" type="password" required />
    
        <label for="email">Email:</label>
        <input id="email" formControlName="email" type="email" required />
    
        <label for="address">Address:</label>
        <textarea id="address" formControlName="address" required></textarea>
    
        <button type="submit" [disabled]="registerForm.invalid">Register</button>
      </form>
    </div>
    

    Add this component to

    app.module.ts

    declarations.

    React

    React Router Dom provides a useNavigate hook to navigate to a certain route.

    import { useNavigate } from "react-router-dom";
    
    const Register = () => {
      const navigate=useNavigate();
      //other code
    
      const handleSubmit = async (e) => {
        e.preventDefault();
    
        try {
          const response = await axios.post(
            "http://localhost:3001/users",
            formData
          );
          console.log("User registered:", response.data);
          navigate('/')
        } catch (error) {
          console.error("Error regarding user:", error);
        }
      };
      return (//other code
      );
    };
    
    export default Register;
    
    Angular

    We can use Router service in register.component.ts.

    import { Router } from '@angular/router';
    
    @Component({
      selector: 'app-register',
      //other code  
    })
    export class RegisterComponent {
      registerForm: FormGroup;
    
      constructor( 
        //other code
        private router:Router
      ) {
        this.registerForm = this.fb.group({//other code});
      }
    
      onSubmit() {
        if (this.registerForm.valid) {
          const formData = {//other code};
    
          this.userService.registerUser(formData).subscribe(
            (response) => {
              console.log('User registered:', response);
              this.router.navigate(['/']);
            },
            (error) => {
              console.error('Error registering user:', error);
            }
          );
        }
      }
    }
    
    React

    To display the username instead of the "Login" link after a successful login, you can use React's state and context to manage the user's login status and information across components. Here's an approach using the React Context API:
    Create a UserContext: This will store the user's login status and information.
    Provide the UserContext to your application: Wrap your application with the context provider.
    Consume the UserContext in the Header component: Display the username if the user is logged in.

    1.Create UserContext.js

    UserContext.js
    import React, { createContext, useState } from 'react';
    
    export const UserContext = createContext();
    
    export const UserProvider = ({ children }) => {
      const [user, setUser] = useState(null);
    
      return (
        <UserContext.Provider value={{ user, setUser }}>
          {children}
        </UserContext.Provider>
      );
    };
    

    React Context is a way to manage state globally.
    createContext lets you create a context that components can provide or read.


    2.Provide the UserContext to your application: Wrap your application with the UserProvider in your App.js file:

    App.js
    import React from 'react';
    import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
    import { UserProvider } from './UserContext';
    import Header from './Header';
    import Login from './Login';
    // Import other components as needed
    
    function App() {
      return (
        <UserProvider>
          <Router>
            <Header />
            <Routes>
              <Route path="/login" element={<Login />} />
              {/* Define other routes as needed */}
            </Routes>
          </Router>
        </UserProvider>
      );
    }
    
    export default App;
    

    3. Consume the UserContext in the Header component
    Modify the Header component to consume the UserContext:

    import React, { useContext } from "react";
    import { UserContext } from './UserContext';
    
    const Header = () => {
      const { user } = useContext(UserContext);
    
      return (
        
              //other code
              {user ? (
                <span>{user.name}</span>
              ) : (
                <Link className="links" to="/login">
                  Login
                </Link>
              )}
      );
    };
    
    export default Header;
    

    Angular

    1. Create UserService
    2. Handling Login Logic in LoginComponent.
    3. Make changes in Header component

    1. UserService

    UserService is responsible for managing user state and handling the login logic.

    1.1BehaviorSubject to hold user state

  • userSubject: This is a BehaviorSubject that holds the current user state. It starts with a value of null meaning no user is logged in initially.
  • user$: This is an observable that other components can subscribe to in order to get updates about the user state.
  • 1.2Define methods to set and get the user

    1.3Define the login method

  • loginUser: This method makes an HTTP POST request to the login endpoint with the user's email and password.
  • tap: This RxJS operator allows us to perform side effects within the observable chain. Here, it updates the user state with the response from the server.
  • user.service.ts
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { BehaviorSubject, Observable } from 'rxjs';
    import { LoginResponse } from '../models/user.model';
    import { tap } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class UserService {
      private apiUrl='http://localhost:3000/user/login'
      private userSubject=new BehaviorSubject(null)
      user$=this.userSubject.asObservable()
      
      constructor(private http:HttpClient) { }
    
      setUser(user:any){
        this.userSubject.next(user)
      }
    
      clearUser(){
        this.userSubject.next(null)
      }
    
      getUser(){
        return this.userSubject.value
      }
    
      loginUser(userData:any):Observable{
        return this.http.post(this.apiUrl,userData)
              .pipe(tap((response:any)=>{
                this.setUser(response.user)
                console.log("here ",response.user)
              }))
      }
    }
    

    2. LoginComponent

    LoginComponent handles user input and calls the UserService to perform the login.

    2.1Define the component with email and password fields and call the login method

    login.component.ts
    import { Component } from '@angular/core';
    import { Router } from '@angular/router';
    import { UserService } from '../../service/user.service';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';
    import { CommonModule } from '@angular/common';
    
    @Component({
      selector: 'app-login',
      standalone: true,
      imports: [FormsModule, ReactiveFormsModule, CommonModule],
      templateUrl: './login.component.html',
      styleUrl: './login.component.scss',
    })
    export class LoginComponent {
      email: string = '';
      password: string = '';
      errorInLogin: any;
    
      constructor(
        private router: Router,
        private userService: UserService
      ) {}
    
      login() {
        this.userService.loginUser(this.email,this.password).subscribe(
          (response) => {
            console.log('User Registered: ', response);
    
            this.router.navigate(['/']);
          },
          (error) => {
            this.errorInLogin = error.error.message;
            }
        );
      }
    
      navigateToRegister(): void {
        this.router.navigate(['/register']);
      }
    }
    
    

    2.2 HTML template for the login form.

    login.component.html
    <div class="login-container">
      <form (ngSubmit)="login()" class="login-form">
        <h2>Login</h2>
        <div class="form-group">
          <label for="email">Email</label>
          <input type="email" name="email" [(ngModel)]="email" id="email" placeholder="Enter your email" required>
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" name="password" [(ngModel)]="password" id="password" placeholder="Enter your password" required>
        </div>
        <br/>
        <div class="error" *ngIf="errorInLogin">{{errorInLogin}}</div>
        <button type="submit" class="login-button">Login</button> <br/><br/>
        <a class="register-link" (click)="navigateToRegister()">Don't have an account? Register</a>
      </form>
    </div>
    

    3. HeaderComponent

    3.1 Define the component to subscribe to user changes.

    header.component.ts
    import { Component } from '@angular/core';
    import {Router} from '@angular/router'
    import { UserService } from '../../service/user.service';
    import { CommonModule } from '@angular/common';
    
    @Component({
      selector: 'app-header',
      standalone: true,
      imports: [
        CommonModule,
        ],
      templateUrl: './header.component.html',
      styleUrl: './header.component.scss',
    })
    export class HeaderComponent {
      user:any;
      constructor(
        private router: Router,
        private userService:UserService
      ) {
        this.userService.user$.subscribe(user=>{
          this.user=user
        })
      }
    
    }
    

    3.2 Update header html

    header.component.html
    <header class="header">
      <div class="left-section">
        <a>Top Authors</a>
        <a (click)="navigateToAllBooks()">Books</a>
      </div>
      <div class="center-section">
        <h1>Writer's Guild</h1>
      </div>
      <div class="right-section">
        <div class="user" *ngIf="user; else loginLink">
          <a>{{user.username}}</a>
        </div>
        <ng-template #loginLink>
          <a (click)="navigateToLogin()">Login</a>
        </ng-template>
        
        <a>Cart</a>
      </div>
    </header>
    

    React

    1. Configure cors middleware in backend.

    npm i cors

    Add the cors middleware to backend

    backend/server.js
    const cors=require('cors')
    app.use(cors())
                  

    2. Use a proxy to route requests through your development server. This can be done by adding a proxy field in your package.json file:

    frontend/package.json
    {
      "name": "your-react-app",
      "version": "1.0.0",
      "proxy": "http://localhost:3000", // specify your backend server's address
      "dependencies": {
        // your dependencies
      }
    }

    3. If it still doesn't work, delete the node-modules and package-lock.json file (not package.json). Close everything, start VS Code again," npm i "", then try again.

    Angular

    1. Configure cors middleware in backend.

    npm i cors

    Add the cors middleware to backend

    backend/server.js
    const cors=require('cors')
    app.use(cors())
                  

    2.Create a proxy.conf.json file in the root of your Angular project --

    proxy.conf.json
    {
      "/api": {
        "target": "http://localhost:3000",
        "secure": false,
        "changeOrigin": true,
        "pathRewrite": {
          "^/api": ""
        }
      }
    }
    

    3. Update options in serve in angular.json.

    angular.json
    "serve": {
      "builder": "@angular-devkit/build-angular:dev-server",
      "options": { //ADD THIS PART
        "proxyConfig": "proxy.conf.json"
      },
      "configurations": {
        //other code
      },
      "defaultConfiguration": "development"
    }