Angular vs React: Covering Every Aspects
Last Updated on
Feb 5, 2025
Both React and Angular are robust JavaScript frameworks for front-end web development. So, the debate of React vs Angular is always on the table when designing user interfaces for web projects. This has also confused companies regarding whether to hire Angular developers or collaborate with a React development company for their projects.
To make informed decisions, companies evaluate vendors and interview developers before hiring them to understand their experience and expertise. Similarly, you have to know your technologies before you use them in your project.
Angular is one of the most popular JavaScript frameworks offering templates or a foundation for developers to build UI components whereas React is a JavaScript library providing predefined code snippets for standardized and rapid development. Both have unique and diverse offerings with their own set of advantages and disadvantages. You have to compare your options and weigh them against your requirements.
This article not only covers a detailed comparison and use cases like every other comparison blog on Angular vs React but also provides a guide for developing sample apps in both technologies. This will give you an idea of the viability of the practical implementation of each option. Let’s get started.
1. Angular vs React: A Detailed Comparison
In this section, we will compare Angular and React in detail against the basic parameters of software development.
1.1 Server-Side Rendering
Angular renders a static view for your app before making it entirely interactive with the help of server-side rendering. It enhances the performance on the server side and reduces traffic between the client and server by using client-side caching with JSON.
You must implement React server-side rendering to make your app SEO-friendly. This Javascript library has some specific functions that can help you with the process. It allows you to render your app by calling the RenderToString function instead of the Render function. React is quite flexible, unlike other rigid JavaScript libraries.
If you prefer not to create DOM attributes for a simple static page generator then React allows you to use renderToStaticMarkup. Facebook is a notable example of a React app with high-speed rendering. It functions completely well even on modest internet connections.
1.2 Performance
React comes with virtual DOM trees. They are lightweight and not built on the server. Therefore, there is less data to load for the browser, resulting in improved runtime performance. Moreover, the unidirectional flow of information reduces the overall workload in the React apps.
But Angular is different, it has a bi-directional flow and every two-way data binding process needs a watcher to monitor the changes. Unless all the watchers are confirmed, every loop remains. This might hurt the performance of the Angular application.
1.3 Component Architecture: Container and Presentation Components
Angular
Container components are used to fetch data and interact with services whereas the presentation components are the ones to whom the data is fed. See the container component example given below, the services are injected into the component to load some data.
@Component({…}) export class UsersComponent implements OnInit{ users: User[]; constructor(private userService: UserService, ) { } ngOnInit(): void { this.userService.getAllUsers().subscribe((data) => { this.users = data; }); } addUser(user: User) { this.userService.addUser(user) .subscribe(() => alert(`User Created Successfully.`)); } } |
Its template will now represent two different presentation components.
<ngs-user-list *ngFor="let user of users | async" . [user]="user" (onButtonClick)="addUser($event)" buttonText="Add User;"> </ngs-user-list> |
Often referred to as smart, these components leverage services to get data and help business logic to transpire. They can pass the data to child components since they represent them. Container components have just one instance in the entire app. And being tied to the context they are used in, they are hardly reused.
Now, let’s take a look at the example of the presentation component.
@Component({...}) export class UserListComponent { @Input() user: User; @Input() buttonText: string; @Output() onButtonClick = new EventEmitter<User>(); } |
Presentation components are pure and simple UI components. They only require some input data to render. An HTML template for this component looks like.
<h2>{{user.title}} </h2> <p>{{user.name}}</p> <div> <h2 class="float-left">{{user.email}}</h2> <button role="button (click)="onButtonClick.emit(user)"> {{buttonText}} </button> </div> |
Presentation components are nothing but an HTML template that you need to fill out with necessary data. You can easily reuse these components in your application. A typical illustration of the component architecture is given below.
The container components retrieve the data from services and use the two-way data binding or @input() to pass this data to its child presentation components. Whenever a significant event occurs like clicking a button, the presentation component notifies the parent container component of the event. In response, the container components implement an appropriate action.
Despite having a single goal to render the template properly, presentation components are independent of their runtime context. This allows developers to reuse them in their applications.
React
The presentation component decides how the data should be displayed on the screen whereas the container component decides which data should be displayed to the user.
The presentation component receives callbacks and the necessary data through props but doesn’t have any connection with the data specifications outside the component. It has a DOM markup and styles of its own. There are no major dependencies on the rest of the application because of this component. The code given below is of the presentation component that is focused on showing an element. The props have provided it with the required data.
import React from "react"; // Presentational component const Users = () => { return ( <div> {users.map((user) => ( <div key={user.id} style={{ marginBottom: "20px", border: "1px solid #ccc", padding: "10px", }}> <h2>UserDetails</h2> <p> <strong>Name:</strong> {user.name} </p> <p> <strong>Username:</strong> {user.username} </p> <p> <strong>Email:</strong> {user.email} </p> <p> <strong>Phone:</strong> {user.phone} </p> <h3>Company</h3> <p> <strong>Company Name:</strong> {user.company.name} </p> </div> ))} </div> ); }; export default Users; |
The container components manage how things work. It doesn’t have its own markup and DOM. It provides data, behavior, and callbacks to the presentation components.
import React, { useState, useEffect } from "react"; import axios from "axios"; // Container component const UsersContainer = () => { const [users, setUsers] = useState([]); useEffect(() => { // Fetch users data after the component mounts axios.get("https://jsonplaceholder.typicode.com/users") .then((response:any) => { setUsers(response.data); }) .catch((error) => { console.error("There was an error fetching users!", error); }); }, []); // Empty dependency array ensures the effect runs once on mount return <Users users={users} />; }; export default UsersContainer; |
1.4 State Management
Managing the state of the UI components, such as text fields and radio buttons, can be challenging. Angular offers a state management library called NGRX that enables reactive state management. It follows the principles of FLUX/REDUX. NGRX stores all the states in a single tree, giving you access to forms across the entire application.
Every component in React has its own state. Therefore, you have to separately handle these components. Otherwise, React developers may encounter a large number of unexpected errors and bugs in a large-scale project.
React offers multiple options for state management. So, if you don’t want to use state management libraries like Redux and Recoil, you can simply manage the state using Hooks.
1.5 Community and Ecosystem Support
One of the major benefits of using Angular is its extensive ecosystem and strong community support. It can assist with troubleshooting and offer support through learning materials like blogs, tutorial videos, templates, and sample projects.
Angular ecosystem consists of a large number of third-party tools and libraries that can provide additional components, functionalities, and utilities to integrate into your app. Angular Material for UI components ngx-translate for internationalization, and NgRx for state management are some of the popular component libraries contributed by the Angular community.
React is an open-source JavaScript library. It has a large and supportive community along with an ecosystem providing a wide range of third-party tools and libraries. They are quite helpful in extending the functionality and enhancing the performance of the application.
Some of the notable tools include CSS modules for styling, Formik for form validation, React Router for routing, and MobX and Redux for state management. React is used for building various JavaScript frameworks and libraries that have become popular nowadays.
1.6 Data Binding
Angular comes with two-way data binding, which means the states will be automatically updated when any changes are implemented in any element of the user interface. In short, the same data is used to update both layers. This helps developers create interactive user interfaces without putting in extra effort or making several callbacks. Two-way data binding works effectively to fulfill the complex requirements of large enterprise software.
React has one-way data binding. So the changes are done in the interface after updating the model state. Similarly, the model state isn’t updated when changes are implemented in the user interface. With uni-directional flow, React developers have more control over their web and mobile applications.
The lack of real-time updates forces developers to handle the complex details of the objects when using React. However, this problem can be resolved by using two-way data binding helpers along with React’s one-way data binding to simplify the parsing and extensive data manipulation.
1.7 Computed Properties
Angular
Despite the fact that Angular doesn’t have any decorator or computed keyword, there are many ways Angular can define computed properties. One of them is RXJS. The code given below shows how Angular uses the RxJS approach to define computed properties.
interface User { id: number; name: string; active: boolean; } @Component({ selector: 'user-stats', template: ` <div> <h2>User Statistics</h2> <p>Active Users: {{ activeCount$ | async }}</p> </div> ` }) export class UserStatsComponent { users$ = new BehaviorSubject<User[]>([]); activeCount$ = this.users$.pipe( map(users => users.filter(user => user.active).length) ); updateUsers(users: User[]) { this.users$.next(users); } } |
users$ (BehaviorSubject): It is a data source with a list of users that needs to be observed.
activeCount$ (Computed Property): This observable uses the RxJS’s map operator to compute the total number of active users. The computed value of the activeCount$ automatically gets updated every time a change is observed in the user$ subject.
This way of defining the computed properties is also identified as a reactive approach. RxJS is quite powerful with out-of-the-box operators. Although the example we considered above is a simple one, RxJS can also help write clean code for complex logic. If any part of the state is already managed with RxJS then it is recommended that you use this approach.
React
React offers a very simple solution for this.
import { makeAutoObservable } from 'mobx'; import {observer} from 'mobx-react'; import React, { Component } from 'react'; ' export class AppStore { constructor() { makeAutoObservable(this); } userName = ""; onUsernameChange = (event) => { this.userName = event.target.value; }; } export class HomeStore { constructor() { makeAutoObservable(this); } counter = 0 increment = () => { this.counter++ }; get counterMessage() { console.log('recompute counterMessage!') return `${this.counter} ${this.counter === 1 ? 'click' : 'clicks'} since last visit` }; } function Input ({ type, label, name, value, onChange }) { return ( <div> <label htmlFor={name}>{label}</label> <input type={type} id={name} name={name} value={value} onChange={onChange} /> </div> ); } |
A computed property binds to the counter and returns a properly pluralized message. Whenever the counter changes, the result of the counterMessage should be cached and recalculated.
const Home = observer(({appStore, homeStore}) => { return ( <div> <Input type='text' label='Edit your name' name='username' value={appStore.username} onChange={appStore.onUsernameChange} /> <span>{homeStore.counterMessage}</span> <button onClick={homeStore.increment}>Click!</button> </div> ); }); export default function App() { const homeStore = new HomeStore(); const appStore = new AppStore(); return <Home homeStore={homeStore} appStore={appStore} />; } |
The increment method and property are referenced from the JSX template. You can drive the input field by allowing the appStore’s method to manage user events and bind them to a value.
1.8 Form Validations
In the case of form validation, there is a difference in React and Angular’s approaches. Angular uses built-in validators or creates custom validators to validate the forms. These validators can be added to the entire form or to individual form controls depending on the specific requirements.
They help check regular expressions, maximum and minimum length, and required fields. In case of errors, Angular provides default error messages to display on the user interface.
There are two techniques to validate a form in Angular: Template-driven form validation and Reactive form validation.
Template-driven Form Validation
In this Angular Form Validation method, we will use the directives from Angular templates to define and validate the form. This approach is an ideal option to validate simple forms.
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)" novalidate> <div> <label for="username">Username:</label> <input type="text" id="username" name="username" ngModel required minlength="3" #username="ngModel" /> <div *ngIf="username.invalid && username.touched"> <small *ngIf="username.errors?.required">Username is required.</small> <small *ngIf="username.errors?.minlength"> Username must be at least 3 characters long. </small> </div> </div> <div> <label for="email">Email:</label> <input type="email" id="email" name="email" ngModel required #email="ngModel" /> <div *ngIf="email.invalid && email.touched"> <small *ngIf="email.errors?.required">Email is required.</small> <small *ngIf="email.errors?.email">Enter a valid email.</small> </div> </div> <button type="submit" [disabled]="userForm.invalid">Submit</button> </form> `, }) export class AppComponent { onSubmit(form: any) { console.log(form.value); } } |
Reactive Form Validation
Reactive form validation in Angular is more of a programmatic approach that helps define and validate complex forms.
import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-root', template: ` <form [formGroup]="userForm" (ngSubmit)="onSubmit()"> <div> <label for="username">Username:</label> <input type="text" id="username" formControlName="username" /> <div *ngIf="userForm.get('username')?.invalid && userForm.get('username')?.touched"> <small *ngIf="userForm.get('username')?.errors?.required">Username is required.</small> <small *ngIf="userForm.get('username')?.errors?.minlength"> Username must be at least 3 characters long. </small> </div> </div> <div> <label for="email">Email:</label> <input type="email" id="email" formControlName="email" /> <div *ngIf="userForm.get('email')?.invalid && userForm.get('email')?.touched"> <small *ngIf="userForm.get('email')?.errors?.required">Email is required.</small> <small *ngIf="userForm.get('email')?.errors?.email">Enter a valid email.</small> </div> </div> <button type="submit" [disabled]="userForm.invalid">Submit</button> </form> `, }) export class AppComponent { userForm: FormGroup; constructor(private fb: FormBuilder) { this.userForm = this.fb.group({ username: ['', [Validators.required, Validators.minLength(3)]], email: ['', [Validators.required, Validators.email]], }); } onSubmit() { console.log(this.userForm.value); } } |
Both Angular form validation methods discussed here are widely used but the decision of picking a suitable way boils down to the complexity of the form. If you are working with a simple form then use the template-driven form validation technique. But if you want better control and scalability then use reactive form validation.
React creates custom validation functions or leverages third-party libraries to achieve form validation. It doesn’t come with a built-in form validation, React developers are responsible for handling the validation logic. Form validations are tracked and error messages are displayed using the state and setState functions.
The code given below shows the use of a custom validation function from React.
import React, { useState } from "react"; const MyForm = () => { const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [email, setEmail] = useState(""); const [errors, setErrors] = useState({}); // Validate form function const validateForm = () => { let errors = {}; if (!firstName) { errors.firstName = "First name is required"; } if (!lastName) { errors.lastName = "Last name is required"; } if (!email) { errors.email = "Email is required"; } else if (!/\S+@\S+\.\S+/.test(email)) { errors.email = "Email is invalid"; } setErrors(errors); return Object.keys(errors).length === 0; }; // Handle form submission const handleSubmit = (event) => { event.preventDefault(); if (validateForm()) { // handle form submission (e.g., send data to server) console.log("Form submitted successfully"); } }; // Handle input change const handleChange = (event) => { const { name, value } = event.target; if (name === "firstName") { setFirstName(value); } else if (name === "lastName") { setLastName(value); } else if (name === "email") { setEmail(value); } }; return ( <form onSubmit={handleSubmit}> <input type="text" name="firstName" value={firstName} onChange={handleChange} required /> {errors.firstName && <div className="error">{errors.firstName}</div>} <input type="text" name="lastName" value={lastName} onChange={handleChange} required /> {errors.lastName && <div className="error">{errors.lastName}</div>} <input type="text" name="email" value={email} onChange={handleChange} required /> {errors.email && <div className="error">{errors.email}</div>} <button type="submit">Submit</button> </form> ); }; export default MyForm; |
In the above example, the validation state of the form is checked with the validateForm function. The error objects are used to track the validation errors and display the error messages to the users.
1.9 Routing
Routing helps provide a better user experience. When the user clicks on any button or element or enters any URL, routing facilitates movement between different parts of the app. Routing gives a new functionality for navigating to the detailed view of every to-do item in your app. For instance, when a user clicks on the to-do item from the list, the app will route them to a new page displaying the item’s title and its completion status.
Angular Routing
Angular Router is the built-in routing solution in this development framework. It helps Angular developers create dynamic and high-performance applications by offering routing features like lazy loading, route guards, and nested routes.
Angular Path-Based Routing
Angular simplifies the routing process by allowing you to define the routes using path URLs. So, Angular would render the component every time a user navigates to the corresponding URL.
We can define appRoutingModule and add a path for the corresponding component like the below code.
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'about', component: AboutComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {} |
Then we need to import AppRoutingModule in App.module file
We will now use lazy loading for Angular routing. Once implemented, it will allow the feature module to load only when it’s necessary. As a result, the app’s performance will improve. This technique can come in handy when handling large apps.
Steps to Implement:
- Create feature modules.
- Configure lazy loading in the routing module using the loadChildren property.
- Define routes in the feature module’s routing module.
In AppRouting we can define a path for the particular module as shown below
const routes: Routes = [ { path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) } ]; |
Angular supports nested routes, where one component has its own child routes. Child routes are defined inside the parent route.
const routes: Routes = [ { path: 'parent', component: ParentComponent, children: [ { path: 'child', component: ChildComponent } ] } ]; |
We can add Guards to protect routes and Query parameters and fragments for additional information.
Guards
const routes: Routes = [ { path: 'profile', component: ProfileComponent, canActivate: [AuthGuard] } ]; |
Query Parameters
this.router.navigate(['/home'], { queryParams: { page: 1, sort: 'asc' } }); |
React Routing
A wide range of routing libraries are available in React. Here we are going to use React Router on a basic React app with four major components including the Home, About, Contact, and NotFound.
With the help of an App component, all four of these components are imported into the file and then executed. Using React Router helps arrange for a seamless transition between these components.
import React from 'react'; import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'; Import Dashboard from ‘./components/Dashboard; Import Services from ‘./components/Services ; Import ReachUs from ‘./components/ReachUs; Import NotFound from ‘./components/NotFound; Import ‘./App.css’; const App = () => { return ( <Router> <div className = “App”> <nav> <u className = “App-header”> <li> <Link to="/">Dashboard</Link> </li> <li> <Link to="/services">Services</Link> </li> <li> <Link to="/reachus">Reach Us</Link> </li> </ul> </nav> <Switch> <Route exact path="/" component={Dashboard} /> <Route path="/services" component={Services} /> <Route path="/reachus" component={ReachUs} /> <Route component={NotFound} /> </Switch> </div> </Router> ); }; export default App; |
2. Sample App Development
Let’s have a look at the sample to-do application development using React and Angular respectively.
2.1 React
Prerequisites: The presence of Node.js and React is required in the system.
Step 1: Create a React app with the following command.
npx create-react-app |
Step 2: Now, we will create components for our Todo application.
- Create a Todo Component which contains logic to list down the to-do items.
- Create a Form component that contains logic to add a new task.
- Create a TodoList component that contains logic for Listing, Mark as Complete, and Delete the task.
Now, create a Todo.js file in components and write the following code in the same.
Create a Todo.js file in components and write the following code in the same. import React from "react"; const Todo = ({ text, todo, completeHandler, deleteHandler }) => { return ( <div className="todo"> <li className={`todo-item ${todo.completed ? "completed" : ""}`}> {text} </li> <button className="complete-btn" onClick={() => completeHandler(todo.id)}> {todo.completed ? ( <i className="fas fa-times"></i> ) : ( <i className="fas fa-check"></i> )} </button> <button className="trash-btn" onClick={() => deleteHandler(todo.id)}> <i className="fas fa-trash"></i> </button> </div> ); }; export default Todo; |
Then create a Form.js file in components and write the following code in the same.
import React, { useState } from "react"; const Form = ({ todos, setTodos }) => { const [todoText, setTodoText] = useState(""); const handleSubmit = (e) => { e.preventDefault(); setTodos([ ...todos, { text: todoText, completed: false, id: Math.random() * 1000 }, ]); setTodoText(""); }; return ( <form> <input type="text" className="todo-input" value={todoText} onChange={(e) => setTodoText(e.target.value)} /> <button className="todo-button" type="submit" onClick={handleSubmit}> <i className="fas fa-plus-square"></i> </button> </form> ); }; export default Form; |
Let’s create a TodoList.js file in components and write the following code in the same.
import React from "react"; import Todo from "./Todo"; const TodoList = ({ todos, setTodos }) => { const deleteHandler = (id) => { setTodos(todos.filter((el) => el.id !== id)); }; const completeHandler = (id) => { setTodos( todos.map((item) => { if (item.id === id) { return { ...item, completed: !item.completed, }; } return item; }) ); }; return ( <div className="todo-container"> <ul className="todo-list"> {todos.map((todo) => ( <Todo key={todo.id} text={todo.text} todo={todo} deleteHandler={deleteHandler} completeHandler={completeHandler} /> ))} </ul> </div> ); }; export default TodoList; |
Step 3: Now, open app.js file in src folder and Write the following code in the same.
import React, { useState } from "react"; import "./App.css"; import Form from "./components/Form"; import TodoList from "./components/TodoList"; const App = () => { const [todos, setTodos] = useState([]); return ( <div> <header>To-do List</header> <Form todos={todos} setTodos={setTodos} /> <TodoList todos={todos} setTodos={setTodos} /> </div> ); }; export default App; |
Step 4: Now add some CSS. Create an App.css file in the src folder and write the code to include CSS.
* { margin: 0; padding: 0; box-sizing: border-box; } body { background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%); color: white; font-family: "Poppins", sans-serif; min-height: 100vh; } header { font-size: 3rem; padding-top: 4rem; font-weight: 600; } header, form { min-height: 15vh; display: flex; justify-content: center; align-items: center; } form input, form button { padding: 0.5rem; font-size: 2rem; border: none; background: white; } form input:focus { outline: none; } form button { color: #ff6f47; background: #f7fffe; cursor: pointer; transition: all 0.3s ease; } form button:hover { background: #ff6f47; color: white; } .todo-container { display: flex; justify-content: center; align-items: center; } .todo-list { min-width: 30%; list-style: none; } .todo { margin: 0.5rem; background: white; font-size: 1.5rem; color: black; display: flex; justify-content: space-between; align-items: center; transition: all 1s ease; } .todo li { flex: 1; } .trash-btn, .complete-btn, .edit-btn { background: rgb(212, 11, 14) ; color: white; border: none; padding: 1rem; cursor: pointer; font-size: 1rem; width: 48px; } .complete-btn { background: #ff6f47 ; } .edit-btn { background: rgb(11, 212, 162); } .todo-item { padding: 0rem 0.5rem; } .fa-trash, .fa-check, .fa-times { pointer-events: none; } .fall { transform: translateY(10rem) rotateZ(20deg); opacity: 0; } .completed { text-decoration: line-through; opacity: 0.5; } .fa-pen { font-size: 25px; } |
Step 5: Update the index.js and index.css files as per the mentioned repository.
Step 6: Update index.html file of public folder as per the mentioned repository.
<link rel="preconnect" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous" /> |
Step 7: Run the application using the following command.
npm start |
Step 8: A new window in the browser will pop up. Add a few tasks here.
Folder Structure:
2.2 Angular
Prerequisites: Install Node and Angular CLI in your system.
Step 1: Create an Angular app with the following command.
> ng new to-do-list |
Move to the project directory using the following command.
> cd to-do-list |
Step 2: Create an interface for To-do items.
export interface ToDoItem { task: string; isCompleted: boolean; } |
Step 3: Create 3 components in src folder using the following commands.
> ng generate component todo > ng generate component form > ng generate component todolist |
- Create a Todo Component which contains logic to list down the to-do items.
- Create a Form component that contains logic to add a new task
- Create a TodoList component that contains logic for listing, mark as complete, and delete the task.
1. ToDo component:
This component is responsible for displaying individual to-do items along with a delete button and a checkbox to mark an item as complete.
template file: We have used a <p> to display the title, a check box to mark the completion of the task, and a delete button.
<div class="center"> <div class="col-9"> <p class="title" [ngStyle]="{ 'text-decoration': isCompleted ? 'line-through' : 'none' }" > {{ title }} </p> </div> <div class="col-1"> <input type="checkbox" [checked]="isCompleted" (change)="isCompleted = !isCompleted" /> </div> <div class="col-2"> <img src="assets/delete.png" alt="Delete" class="delete-icon" (click)="onDelete()" /> </div> </div> |
ts file: ts file contains 2 input properties that are title and isCompleted, along with this there is a delete method that emits the event to a parent component.
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'app-to-do', standalone: true, imports: [CommonModule], templateUrl: './to-do.component.html', styleUrl: './to-do.component.css', }) export class ToDoComponent { @Input() title: string = ''; @Input() isCompleted: boolean = false; @Output() deleteTodoItem = new EventEmitter<void>(); onDelete(): void { this.deleteTodoItem.emit(); } } |
2. to-do-list: display ToDo-list
template file: use *ngFor directive to iterate each element in the toDoList.
<div *ngFor="let item of toDoList"> <app-to-do [title]="item.task" [isCompleted]="item.isCompleted" (deleteTodoItem)="deleteTodoItem(item)" ></app-to-do> </div> |
ts file: ts file takes to-do list as input and a delete method which emits the event to the parent component.
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ToDoComponent } from '../to-do/to-do.component'; import { CommonModule } from '@angular/common'; import { ToDoItem } from '../../ToDoItem'; @Component({ selector: 'app-to-do-list', standalone: true, imports: [ToDoComponent, CommonModule], templateUrl: './to-do-list.component.html', styleUrl: './to-do-list.component.css', }) export class ToDoListComponent { @Input() toDoList: ToDoItem[] = []; @Output() deleteItem = new EventEmitter<ToDoItem>(); deleteTodoItem(todoItem: any): void { this.deleteItem.emit(todoItem); } } |
3. Form component
Template file: contains an input element to store the value of the to-do task and an add button.
<div class="row"> <div class="col-9"> <input type="text" [(ngModel)]="toDoItem.task" placeholder="Enter task" maxlength="100"/> </div> <div class="col-2"> <img src="assets/icons8-add-48.png" alt="Add" class="add-icon" (click)="onAddTodoItem()" /> </div> </div> |
Ts file: There is a method to push an item to the parent component so that it can be added to a to-do list.
import { ToDoItem } from './../../ToDoItem'; import { Component, EventEmitter, Output } from '@angular/core'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-form', standalone: true, imports: [FormsModule], templateUrl: './form.component.html', styleUrl: './form.component.css', }) export class FormComponent { @Output() addTodoItem = new EventEmitter<ToDoItem>(); toDoItem: ToDoItem = { task: '', isCompleted: false }; onAddTodoItem() { this.addTodoItem.emit(this.toDoItem); this.toDoItem = { task: '', isCompleted: false }; } } |
4. App component:
<main class="main"> <div class="container"> <h1>To-Do List</h1> <app-form (addTodoItem)="addTodoItem($event)"></app-form><br> <app-to-do-list [toDoList]="toDoList" (deleteItem)="deleteTodoItem($event)"></app-to-do-list> </div> </main> <router-outlet /> |
Ts file: implement logic to add and delete items from the to-do list.
import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { ToDoListComponent } from '../Components/to-do-list/to-do-list.component'; import { FormComponent } from '../Components/form/form.component'; import { ToDoItem } from '../ToDoItem'; import { CommonModule } from '@angular/common'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet, ToDoListComponent, FormComponent, CommonModule], templateUrl: './app.component.html', styleUrl: './app.component.css', }) export class AppComponent { title = 'todo-list-app'; toDoList: ToDoItem[] = [ ]; addTodoItem(item: ToDoItem) { this.toDoList.push(item); } deleteTodoItem(todoItem: any): void { this.toDoList = this.toDoList.filter((item) => item !== todoItem); } } |
Step 4: Now Add some CSS. Create an App.css file in the src folder and write the code to include CSS.
Step 5: Run your application using the following command.
npm start |
Step 6: Test your application in the browser, by adding/deleting a task and marking the task as complete.
Folder Structure:
3. Best Practices Comparison of React vs Angular
After comparing the Angular and React technologies with basic parameters and going through their development guides, it’s time to discuss their development best practices. Some practices are implemented with almost all technologies, the implementation of these practices may vary a little.
3.1 Reusable Components
Components in React are small, simple, self-contained, and function-specific. They are designed in a way that a single component either renders a specific section of the application or alters its behavior. This makes it easy to test and maintain React components, allowing for their reuse across different applications. Reusable React components that can perform standard functions for your app are available at community ReactJS development forums.
Such standard and reusable components are kept small and specific, which helps improve the performance of the application. Because they use minimal space in your project, it leaves room for innovation. Although some reusable components might take more space if their functions are complex but critical to the app. In such cases, you have to consider your requirements and proceed accordingly.
Building reusable components is an inherent practice in Angular development. It is based on the requirement of the same component at multiple places or in multiple instances. Reusable components in Angular maintain consistent performance and functionality throughout the application. Even in the case of redesigning, you don’t need to rewrite all of your components when you have reusable ones at your disposal.
3.2 Component Communication
Here, we are discussing component communication between parent to child, child to parent, and sibling components in Angular and React.
Angular: Parent to Child
Using the input decorator, we can pass data from the parent component to the child component in Angular. The code given below uses an input decorator to pass data from the parent component to the child component.
Parent component- Here we are sending a text message from the parent.
parent.component.ts
@Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrl: './parent.component.css', }) export class ParentComponent { responseMessage = 'Message From Parent'; } |
In the Angular child component, the text message is passed by property binding. To modify the text message, we have to use the input text box from the parent component.
Parent.component.html
<div class="bg-primary p-2"> <p class="text-white m-2 display-4">Parent</p> <div> <input type="text" class="form-control w-25 m-2" [(ngModel)]="responseMessage" /> </div> <div class="child_body"> <app-child class="w-100" [message]="responseMessage"></app-child> </div> </div> |
Child component- Here we will have an @Input decorator which will be used in the parent to send data.
child.component.ts
@Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrl: './child.component.css', }) export class ChildComponent { @Input() message: string | null = null; } |
Using interpolation binding in the code below, we can display the text message. If it is updated in the parent component then it will be automatically updated in the child component as well.
Child.component.html
<div class="w-100 child_body"> <p class="text-white m-2 display-4">Child</p> <p>Message : {{ message }}</p> </div> |
Output:
Angular: Child to Parent
In Angular, the @Output decorator is used for communication from the child to the parent component.
Child component- The child component uses EventEmitter to emit events and data to the parent component. The @Output decorator is used to expose the Event Emitter which allows the parent component to list and handle emitted events.
child.component.ts
@Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrl: './child.component.css', }) export class ChildComponent { @Output() changeMessage = new EventEmitter<string>(); } Child.component.html <div class="w-100 child_body"> <p class="text-white m-2 display-4">Child</p> <input type="text" #message class="form-control w-25 m-2" (keyup)="changeMessage.emit(message.value)" /> </div> |
Parent Component
parent.component.ts
@Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrl: './parent.component.css', }) export class ParentComponent { responseMessage = ''; updateMessage(e: any): void { this.responseMessage = e; } } |
To update the message in the parent component, we use an output decorator named changeMessage in the code below.
Parent.component.html
<div class="bg-primary p-2"> <p class="text-white m-2 display-4">Parent</p> <div> <p class="text-white">Message : {{ responseMessage }}</p> </div> <div class="child_body"> <app-child class="w-100" (changeMessage)="updateMessage($event)" ></app-child> </div> </div> |
Output:
Angular: Passing Data between Siblings
There are multiple ways to share data between sibling components,
1) Using RxJS (common and recommended approach)
2) Using the parent component as a mediator.
3) Using State management.
4) Using Local or Session storage.
5) Using Angular Routing
We can create a service that acts as a shared data store.
Use Subject or BehaviorSubject from RxJS, which is recommended to share data reactively.
shared-data.service.ts
This service will have a behaviorSubject from the RxJS library that used to store the current value and emit it to new subscribers
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class SharedDataService { private data = new BehaviorSubject<{ message: string; from: string }>({ message: '', from: '', }); sharedData$ = this.data.asObservable(); setData(newData: { message: string; from: string }) { this.data.next(newData); } getData() { return this.data; } } |
Child 1 component- With this code, we inject the SharedDataService and check if the message is from the child 2 component before it is updated. Once the message is updated through changeMessage, it is sent to the child 2 component.
child1.component.ts
@Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrl: './child.component.css', }) export class ChildComponent implements OnInit { message: string = ''; constructor(private sharedService: SharedDataService) {} ngOnInit(): void { this.sharedService.getData().subscribe((data) => { if (data.from == 'c2') this.message = data.message; }); } changeMessage(msg: string): void { this.sharedService.setData({ message: msg, from: 'c1' }); } } |
child1.component.html
<div class="w-100 child_body"> <p class="text-white m-2 display-4">Child 1</p> <input type="text" #inpMessage class="form-control w-50 m-2" (keyup)="changeMessage(inpMessage.value)" /> <div> <p>Message From Child 2: {{ message }}</p> </div> </div> |
Child 2 component- The code below injects the ShareDataService and checks if the message is from the child 1 component before it is updated. The message is sent to the child 1 component after updating it with the changeMessage.
child2.component.ts
@Component({ selector: 'app-child2', templateUrl: './child2.component.html', styleUrl: './child2.component.css', }) export class Child2Component implements OnInit { message: string = ''; constructor(private sharedService: SharedDataService) {} ngOnInit(): void { this.sharedService.getData().subscribe((data) => { if (data.from == 'c1') this.message = data.message; }); } changeMessage(msg: string): void { this.sharedService.setData({ message: msg, from: 'c2' }); } } |
child2.component.html
<div class="w-100 child_body"> <p class="text-white m-2 display-4">Child 2</p> <input type="text" #inpMessage class="form-control w-50 m-2" (keyup)="changeMessage(inpMessage.value)" /> <div> <p>Message From Child 1: {{ message }}</p> </div> </div> |
Output:
React: Parent to Child Communication
Parent components in React applications use a special property called Props to communicate with the child components. HTML attributes are leveraged to pass props that are read-only from parent-to-child components. Let’s look at an example to understand this.
ParentComponent.tsx
import React, { useState } from "react"; import ChildComponent from "./ChildComponent"; const styles: { [key: string]: React.CSSProperties } = { parentContainer: { backgroundColor: "#0078FF", display: "flex", flexDirection: "column", justifyContent: "flex-start", alignItems: "flex-start", color: "white", padding: "10px", }, parentContent: { marginBottom: "20px", width: "100%", }, parentHeader: { fontSize: "3rem", }, input: { padding: "15px", width: "30%", borderRadius: "8px", border: "2px solid white", fontSize: "1.2rem", }, }; const ParentComponent: React.FC = () => { const [message, setMessage] = useState<string>("message from parent"); return ( <div style={styles.parentContainer}> <div style={styles.parentContent}> <h1 style={styles.parentHeader}>Parent</h1> <input type="text" value={message} onChange={(e) => { setMessage(e.target.value); }} style={styles.input} placeholder="Type a message..." /> </div> <ChildComponent message={message} /> </div> ); }; export default ParentComponent; |
Child component- Here we will have received a message from the parent component as props.
ChildComponen.tsx
import React from "react"; const styles: { [key: string]: React.CSSProperties } = { childContainer: { backgroundColor: "green", color: "white", borderRadius: "10px", width: "100%", marginTop: "20px", textAlign: "center", }, childHeader: { fontSize: "3rem", marginBottom: "10px", }, childMessage: { fontSize: "1.2rem", }, }; interface ChildProps { message: string; } const ChildComponent: React.FC<ChildProps> = ({ message }) => { return ( <div style={styles.childContainer}> <h1 style={styles.childHeader}>Child</h1> <p style={styles.childMessage}>Message: {message}</p> </div> ); }; export default ChildComponent; |
Open App.tsx file in src folder and modify the following code in the same.
import React from "react"; import "./App.css"; import ParentComponent from "./components/ParentComponent"; const App = () => { return <ParentComponent />; }; export default App; |
Run the application using the following command.
npm start |
Output:
React: Child to Parent Communication
For child-to-parent communication in React, a callback function is passed as a prop from parent to child component. Whenever the child wants to communicate with the parent, it has to call this function.
Child component- Whenever the child component needs to send some data or trigger an event, it just has to call the function passed to it from the parent.
ChildComponent.tsx
import React from 'react' interface ChildComponentProps { setMessage?: (value: string) => any; } const styles: { [key: string]: React.CSSProperties } = { childContainer: { backgroundColor: "green", color: "white", borderRadius: "10px", textAlign: "center", padding: "20px", }, childHeader: { fontSize: "3rem", margin: "10px", }, input: { padding: "15px", width: "30%", borderRadius: "8px", border: "2px solid white", fontSize: "1.2rem", }, }; const ChildComponent: React.FC<ChildComponentProps> = ({ setMessage }) => { const [responseMessage, setResponseMessage] = React.useState(''); return ( <div style={styles.childContainer}> <h1 style={styles.childHeader}>Child</h1> <input type="text" className="form-control w-25 m-2" value={responseMessage} onChange={(e: any) => { setResponseMessage(e.target.value) setMessage?.(e.target.value); }} style={styles.input} placeholder="Type a message..." /> </div> ) } export default ChildComponent |
Parent Component- The parent component renders the ChildComponent and passes the setMessage function to it through the setMessage prop.
The parent also displays the response message it receives from the child.
ParentComponent.tsx
import React from 'react' import ChildComponent from './ChildComponent' const styles: { [key: string]: React.CSSProperties } = { parentContainer: { backgroundColor: "#0078FF", color: "white", padding: "10px", }, parentContent: { marginBottom: "20px", width: "100%", }, parentHeader: { fontSize: "3rem", }, MessageText: { fontSize: "1.2rem", }, }; const ParentComponent = () => { const [responseMessage, setResponseMessage] = React.useState(''); const setMessage = (value: any) => { setResponseMessage(value); } return ( <div style={styles.parentContainer}> <div style={styles.parentContent}> <h1 style={styles.parentHeader}>Parent</h1> <p style={styles.MessageText}>Message : {responseMessage}</p> </div> <ChildComponent setMessage={setMessage} /> </div> ) } export default ParentComponent |
Open App.tsx file in src folder and modify the following code in the same.
import React from "react"; import "./App.css"; import ParentComponent from "./components/ParentComponent"; const App = () => { return <ParentComponent />; }; export default App; |
Run the application using the following command.
npm start |
Output:
React: Passing Data between Siblings
Components present at the same level of the component tree are known as sibling components. Communication between them is a frequent requirement. It is made possible by the parent component as it manages shared state and passes appropriate callbacks to the children components.
Parent Component- The callback functions and messages are handled by the parent component. It also renders the Child1Component and Child2Component.
ParentComponent.tsx
import React, { useState } from "react"; import Child1Component from "./Child1Component"; import Child2Component from "./Child2Component"; const styles: { [key: string]: React.CSSProperties } = { parentContainer: { display: "flex", flexDirection: "row", justifyContent: "flex-start", alignItems: "flex-start", color: "white", padding: "10px", }, }; const ParentComponent: React.FC = () => { const [messageChild1, setMessageChild1] = useState<string>(""); const [messageChild2, setMessageChild2] = useState<string>(""); const setChild1Message = (value: string) => { setMessageChild2(value); } const setChild2Message = (value: string) => { setMessageChild1(value); } return ( <div style={styles.parentContainer}> <Child1Component message={messageChild1} setMessage={setChild1Message} /> <Child2Component message={messageChild2} setMessage={setChild2Message} /> </div> ); }; export default ParentComponent; |
Child1Component Component: Once the callback function and message are passed to it from the parent, the child1 component can call the function to send data, trigger an event and display the message that passes from the child2 component
Child1Component.tsx
import React, { useState } from 'react' const styles: { [key: string]: React.CSSProperties } = { childContainer: { backgroundColor: "#339567", color: "white", borderRadius: "10px", width: "100%", margin: "5px", textAlign: "center", }, childHeader: { fontSize: "3rem", marginBottom: "10px", }, childMessage: { fontSize: "1.5rem", }, input: { padding: "15px", width: "50%", borderRadius: "8px", border: "2px solid white", fontSize: "1.2rem", }, }; interface Child1Props { message: string; setMessage: (value: string) => void } const Child1Component: React.FC<Child1Props> = ({ message, setMessage }) => { const [messagealue, setMessageValue] = useState<string>(""); return ( <div style={styles.childContainer}> <h1 style={styles.childHeader}>Child 1</h1> <input type="text" value={messagealue} onChange={(e) => { setMessageValue(e.target.value); setMessage(e.target.value); }} style={styles.input} placeholder="Type a message..." /> <p style={styles.childMessage}><b>Message From Child 2 : </b>{message}</p> </div> ) } export default Child1Component |
Child2Component Component: The parent passes the callback function and message to the child2, allowing the child2 component to call the function to send data, trigger an event and display the message that passes from the child1 component.
Child2Component.tsx
import React, { useState } from 'react' const styles: { [key: string]: React.CSSProperties } = { childContainer: { backgroundColor: "#339567", color: "white", borderRadius: "10px", width: "100%", margin: "5px", textAlign: "center", }, childHeader: { fontSize: "3rem", marginBottom: "10px", }, childMessage: { fontSize: "1.5rem", }, input: { padding: "15px", width: "50%", borderRadius: "8px", border: "2px solid white", fontSize: "1.2rem", }, }; interface Child2Props { message: string; setMessage: (value: string) => void } const Child2Component: React.FC<Child2Props> = ({ message, setMessage }) => { const [messagealue, setMessageValue] = useState<string>(""); return ( <div style={styles.childContainer}> <h1 style={styles.childHeader}>Child 2</h1> <input type="text" value={messagealue} onChange={(e) => { setMessageValue(e.target.value); setMessage(e.target.value); }} style={styles.input} placeholder="Type a message..." /> <p style={styles.childMessage}><b>Message From Child 1 : </b> {message}</p> </div> ) } export default Child2Component |
Open App.tsx file in src folder and modify the following code in the same.
import React from "react"; import "./App.css"; import ParentComponent from "./components/ParentComponent"; const App = () => { return <ParentComponent />; }; export default App; |
Run the application using the following command.
npm start |
Output:
React: Context API
Passing data using props in large and complex React applications can cause very inconvenience. In such instances, ContextAPI provides a more efficient way to pass data between components throughout the component tree. There is no need to manually pass down the props at each level. ContextAPI offers a better way to pass data from a parent component to any deeply nested child component in the component tree.
In the code above, the <Employee> component passed the data to the grandchild <EmployeeProfile> component using the React feature of context API. Props were not used anywhere in this communication.
3.3 Implementation of ES6 Function
ECMAScript 6, popularly known as ES6 is a web development feature offering new syntax to write clear and modern programs. It’s continuously updated with the latest features and functionalities. Object Literals string interpolation, Arrow Functions, and Let and Const are some of the features of ES6 that help make JavaScript programming easy for Angular developers.
In React, ES6 functions are used to pass object properties. All the props can be automatically inserted with the use of {…props} syntax between open and closed tags.
let propertiesList = { className: "my-favorite-props ", id: "myFav", content: "Hello my favourite!" }; let SmallDiv = props => <div {... props} />; let mainDiv = < SmallDiv props={propertiesList} />; view raw es6_spread_function.js hosted with by GitHub |
Using the Spread function can be useful when there aren’t any ternary operations or you don’t just have to pass HTML tag attributes and content. Also, it’s not ideal to use the spread function when the functions are used repetitively, in the presence of dynamic properties, when object properties and arrays are required, or when nested tags are needed during rendering.
4. Developer Opinion
Nitin Gupta – As a Project Manager, I value Angular for its numerous built-in functionalities and other comprehensive offerings. Managing large-scale apps has become easy, thanks to its dependency injection and TypeScript-based strong typing. However, if you are looking for faster development cycles, then React can provide you with the required flexibility and the ability to create reusable components. I would choose React for projects requiring rapid development and a lightweight approach. However I would prefer Angular for projects prioritizing maintainability and scalability.
Dipal Patel – I think both Angular and React are valuable as they cater to different project requirements. When working with a large team, you need Angular’s structured environment. Its two-way data binding simplifies the synchronization between the model and the view. Meanwhile, Virtual DOM from React can enhance the performance of dynamic user interfaces. If you have to choose one, I’d pick Angular to handle enterprise-level apps because of its robust architecture whereas React is my go-to option for small projects where speed is critical.
Badal Patel – In my experience, choosing between Angular and React often comes down to the specific use case. Angular comes with a modular architecture and a robust CLI which helps boost the productivity of enterprise-grade applications. On the other hand, the component-based architecture from React provides better flexibility and faster rendering times. So, when working on a project that needs frequent updates, I’d choose React for its integration capabilities and huge libraries. On the contrary, Angular is an ideal option for projects that demand robust structure and consistency.
5. Conclusion
Both React and Angular are unique technologies, each suited for different use cases. While React outperforms Angular, the latter provides better scalability and can handle unexpected spikes in web traffic. So, each comes with a different set of features and capabilities, making each technology robust in their respective areas. The decision of choosing the suitable tool depends completely on your goals and requirements.
Which one of these technologies did you find helpful or would pick for your project? Let us know your reasons in the comments section below.
FAQs
Is React better than Angular?
React provides better backward compatibility, bundle size, and performance than Angular. Moreover, you can save a lot of time and money by reusing components in React’s component-driven architecture.
Why choose Angular or React?
Choose Angular if you want to keep all your data in sync at every level. That’s possible because of its two-way data binding feature. Meanwhile, if you are simply looking for a better cross-platform solution or want something with an easy learning curve then React is the right choice.
Is Angular easier than React?
React uses plain JavaScript and small package sizes. So, it is quite fast and easy to learn. On the contrary, Angular comes with a wide range of built-in functionalities, making the structure a little complicated. Therefore, Angular has a steep learning curve.
Does Google use Angular?
Google has developed this open-source JavaScript framework. So, it’s obvious that Google would use Angular in many of its internal projects such as Google Fiber, and Google AdWords.
Comments