Last modified: July 18, 2023
This article is written in: 🇺🇸
A software framework is a pre-written app skeleton on which you may further develop. It is a collection of files and folders to which you may modify as well as add your files and folders. A framework addresses following development issues:
We already learned how to create a simple website using just three files: index.html, style.css and script.js. This setup works well for a simple website, but quickly becomes cumbersome when you want to add more functionality.
As with everything in life, there are benefits and costs to using frameworks.
There are, in reality, hundreds, if not thousands, different frameworks. Anyone, including you and me, may create their own framework. Fortunately, there are just a few that are widely popular, so it's not too bad. Wikipedia mentions over 20 frameworks and their comparisons, although the vast majority of those mentioned are either dead or outdated.
Links:
React is a JavaScript library for building user interfaces, often referred to as a framework due to its extensive ecosystem. Unlike a framework, which provides a structured approach to building applications, a library like React allows for more flexibility in implementation.
Pros:
Cons:
React components are the building blocks of a React application. They can be thought of as custom, reusable HTML elements, and they encapsulate their own structure, style, and behavior.
Components in React are written using JSX, an extension to JavaScript that allows writing HTML-like syntax within JavaScript. A component is a JavaScript function or class that returns a React element.
Example of a Class Component:
import React from 'react';
import ReactDOM from 'react-dom';
class HelloWorld extends React.Component {
render() {
return (
<div>
<h1>Hello World</h1>
This is a paragraph
</div>
);
}
}
ReactDOM.render(<helloworld></helloworld>, document.getElementById('root'));
Props (short for properties) are read-only attributes used to pass data from parent components to child components. They are passed to the component as an argument.
Example of Passing Props:
function App() {
return <paragraph text="This is a paragraph"></paragraph>;
}
function Paragraph(props) {
return {props.text};
}
Props can also be passed between opening and closing tags of a component, accessible via props.children
.
function App() {
return (
<container>
<div>
<h1>Hello World</h1>
This is a paragraph
</div>
</container>
);
}
function Container(props) {
return <div>{props.children}</div>;
}
Mounting is the process of rendering a React component to the DOM. It involves several lifecycle methods:
constructor(props)
- Called before the component is mounted.render()
- Defines what the component UI looks like.componentDidMount()
- Called after the component is mounted.componentWillUnmount()
- Called before the component is unmounted and destroyed.componentDidCatch()
- Called when an error is thrown during rendering.State is a way to manage dynamic data in a React component. It is an object that determines how that component renders and behaves.
Example of Using State:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<h1>Hello World</h1>
This is a paragraph
{this.state.count}
</div>
);
}
}
React supports all standard DOM events. Below is an example of handling a click event:
class App extends React.Component {
render() {
return (
<div>
<h1>Hello World</h1>
This is a paragraph
<button =="" onclick="{()"> alert('Hello World')}>Click me</button>
</div>
);
}
}
React uses a virtual DOM to improve performance. When a component’s state or props change, React creates a new virtual DOM tree and compares it to the previous one. Only the changed elements are updated in the real DOM.
Steps:
This process can lead to performance improvements, especially for applications with frequent updates.
Conditional rendering in React can be done using JavaScript conditional statements like if
or ternary operators.
Example of Conditional Rendering:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clicked: false
};
}
render() {
return (
<div>
<h1>Hello World</h1>
This is a paragraph
<button =="" onclick="{()"> this.setState({ clicked: !this.state.clicked })}>
{this.state.clicked ? 'Clicked' : 'Not clicked'}
</button>
</div>
);
}
}
Hooks are functions that let you use state and other React features without writing a class. They work inside functional components and provide a way to reuse stateful logic.
The useState
hook allows you to add state to functional components.
Example of useState:
function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello World</h1>
This is a paragraph
<button =="" onclick="{()"> setCount(count + 1)}>
{count}
</button>
</div>
);
}
The useEffect
hook lets you perform side effects in functional components. It runs after the component renders.
Example of useEffect:
function App() {
useEffect(() => {
console.log('Component rendered');
});
return (
<div>
<h1>Hello World</h1>
This is a paragraph
</div>
);
}
The useRef
hook is used to access DOM elements directly.
Example of useRef:
function App() {
const inputRef = useRef();
const onButtonClick = () => {
inputRef.current.focus();
};
return (
<div>
<h1>Hello World</h1>
This is a paragraph
<input ref="{inputRef}" type="text"/>
<button onclick="{onButtonClick}">Focus the input</button>
</div>
);
}
The useReducer
hook is an alternative to useState
for managing complex state logic.
Example of useReducer:
function App() {
const [count, dispatch] = useReducer((state, action) => {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
throw new Error();
}
}, 0);
return (
<div>
<h1>Hello World</h1>
This is a paragraph
<button =="" onclick="{()"> dispatch({ type: 'increment' })}>
{count}
</button>
<button =="" onclick="{()"> dispatch({ type: 'decrement' })}>
{count}
</button>
</div>
);
}
The useContext
hook allows you to access the context object and subscribe to its changes.
Example of useContext:
function App() {
const theme = useContext(ThemeContext);
return (
<div background:="" color:="" style="{{" theme.background,="" theme.foreground="" }}="">
<h1>Hello World</h1>
This is a paragraph
</div>
);
}
The useMemo
hook memoizes expensive computations to avoid re-executing them on every render.
Example of useMemo:
function App() {
const expensiveComputation = () => {
let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += i;
}
return sum;
};
const memoizedComputation = useMemo(expensiveComputation, []);
return (
<div>
<h1>Hello World</h1>
This is a paragraph
{memoizedComputation}
</div>
);
}
The Context API is used for managing global state across a React application, especially for passing data through the component tree without having to pass props down manually at every level.
Creating a Context:
const ThemeContext = React.createContext('light');
function App() {
return (
<themecontext.provider value="dark">
<toolbar></toolbar>
</themecontext.provider>
);
}
function Toolbar() {
return (
<div>
<themedbutton></themedbutton>
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button '#333'="" '#fff'="" :="" ?="" background:="" style="{{" theme="dark" }}="">Themed Button</button>;
}
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.
Example of an Error Boundary:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
function App() {
return (
<errorboundary>
<componentthatmayerror></componentthatmayerror>
</errorboundary>
);
}
HOCs are functions that take a component and return a new component. They are used to reuse component logic.
Example of a Higher-Order Component:
function withLoading(Component) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (!isLoading) return <component {...props}=""></component>;
return Loading...;
};
}
function MyComponent(props) {
return <div>Data: {props.data}</div>;
}
const MyComponentWithLoading = withLoading(MyComponent);
function App() {
return <mycomponentwithloading data="Some data" isloading="{true}"></mycomponentwithloading>;
}
Render props is a technique for sharing code between React components using a prop whose value is a function.
Example of Render Props:
class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div '100vh'="" height:="" onmousemove="{this.handleMouseMove}" style="{{" }}="">
{this.props.render(this.state)}
</div>
);
}
}
function App() {
return (
<mouse render="{({" x,="" y="" })=""> (
<h1>The mouse position is ({x}, {y})</h1>
)} />
);
}
Portals provide a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
Example of Using Portals:
import ReactDOM from 'react-dom';
function Modal({ children }) {
return ReactDOM.createPortal(
<div classname="modal">
{children}
</div>,
document.getElementById('modal-root')
);
}
function App() {
return (
<div>
<h1>Hello World</h1>
<modal>
<h2>This is a modal</h2>
</modal>
</div>
);
}
Fragments let you group a list of children without adding extra nodes to the DOM.
Example of Using Fragments:
function App() {
return (
<>
<h1>Hello World</h1>
This is a paragraph
);
}
React.memo
is a higher-order component that memoizes the result of a component rendering, preventing unnecessary re-renders.
Example of Using React.memo:
const MyComponent = React.memo(function MyComponent({ count }) {
console.log('Rendered MyComponent');
return <div>{count}</div>;
});
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button =="" onclick="{()"> setCount(count + 1)}>Increment</button>
<mycomponent count="{count}"></mycomponent>
</div>
);
}
Concurrent Mode is a set of new features that help React apps stay responsive and gracefully adjust to the user’s device capabilities and network speed.
Example of Enabling Concurrent Mode:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<app></app>);
Suspense lets you wait for some code to load and declaratively specify a loading state.
Example of Using Suspense:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function App() {
return (
<div>
<suspense fallback="{<div">Loading...</suspense></div>}>
<othercomponent></othercomponent>
);
}
Refs provide a way to access the DOM nodes or React elements created in the render method. The useImperativeHandle
hook customizes the instance value that is exposed when using refs.
Example of Using Refs and useImperativeHandle:
function CustomInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref="{inputRef}"/>;
}
CustomInput = forwardRef(CustomInput);
function App() {
const inputRef = useRef();
return (
<div>
<custominput ref="{inputRef}"></custominput>
<button =="" onclick="{()"> inputRef.current.focus()}>Focus Input</button>
</div>
);
}
Let's take a look at a minimal web app that uses React. This guide will walk you through setting up the app from scratch, including installing necessary dependencies, setting up project structure, configuring Babel and Webpack, and writing the actual React code.
To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.
npm --version
npm init
package.json
file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.At the end of this setup, your project will have the following structure:
Project Root
│
├── src
│ └── App.js
│
├── dist
│ └── index.html
│
├── package.json
├── webpack.config.js
└── .babelrc
Transpilers are used to convert code between various programming languages or versions of the same programming language. If we use any modern JavaScript features or frameworks, we can use a transpiler to transform our code to an earlier version of JavaScript that is supported by all web browsers.
The transpiler used in this example is Babel. To install it, use the following commands:
npm install --save-dev babel-loader
npm install --save-dev @babel/preset-react
npm install --save-dev @babel/preset-env
Create a .babelrc
file to configure Babel. This file, in JSON format, describes how to transpile JavaScript code:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Webpack combines all NPM dependencies into a single bundle.js
that can run in a browser. Use the following command to install it:
npm install --save-dev webpack webpack-cli webpack-dev-server
A special file named webpack.config.js
is needed to configure Webpack. The following is an example of this file:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: path.resolve(__dirname, './src/App.js'),
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
}]
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
devServer: {
static: path.resolve(__dirname, './dist'),
port: 8000,
hot: true
},
};
Two libraries are needed to use React: react
and react-dom
. Use the following command to install them:
npm install --save react react-dom
In the src/App.js
file, include the following React script:
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return <h1>Hello World</h1>;
};
ReactDOM.render(<app></app>, document.getElementById('root'));
In addition to JavaScript, an HTML file is required to render the React code. Place the index.html
file in the dist
folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="ie=edge" http-equiv="X-UA-Compatible"/>
<title>Minimal React App</title>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
scripts
section of your package.json
file:
"scripts": {
"start": "webpack serve --mode development"
}
npm start
By following these steps, you will have set up a minimal React web application with Babel and Webpack.
Vue.js is a progressive JavaScript framework used for building user interfaces and single-page applications. It is designed to be incrementally adoptable, meaning you can use as much or as little of Vue as you need.
Pros:
Cons:
Vue components are reusable instances with their own structure, style, and behavior. They are defined using a single file component (SFC) format with the .vue
extension.
Example of a Vue Component:
<template>
<div>
<h1>Hello World</h1>
This is a paragraph
</div>
</template>
<script>
export default {
name: 'HelloWorld'
}
</script>
<style scoped="">
h1 {
color: blue;
}
</style>
Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomes a property on that component instance.
Example of Passing Props:
<template>
<div>
<paragraph text="This is a paragraph"></paragraph>
</div>
</template>
<script>
import Paragraph from './Paragraph.vue';
export default {
components: {
Paragraph
}
}
</script>
<template>
{{ text }}
</template>
<script>
export default {
props: ['text']
}
</script>
Directives are special tokens in the markup that tell the library to do something to a DOM element. Vue has built-in directives such as v-bind
, v-model
, and v-if
.
Example of Using Directives:
<template>
<div>
<input v-model="message"/>
{{ message }}
<p v-if="seen">Now you see me
</div>
</template>
<script>
export default {
data() {
return {
message: '',
seen: true
}
}
}
</script>
Computed properties are cached based on their dependencies and only re-evaluate when necessary. They are useful for complex data manipulations.
Example of Computed Properties:
<template>
<div>
Original message: {{ message }}
Reversed message: {{ reversedMessage }}
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello World'
}
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('');
}
}
}
</script>
Methods are functions that are executed when events occur. They are useful for handling user inputs or other interactions.
Example of Methods:
<template>
<div>
<button @click="greet">Greet</button>
</div>
</template>
<script>
export default {
methods: {
greet() {
alert('Hello World');
}
}
}
</script>
Vue components have a series of lifecycle hooks that allow you to add code at specific stages.
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
Example of Lifecycle Hooks:
<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello World'
}
},
created() {
console.log('Component created');
},
mounted() {
console.log('Component mounted');
}
}
</script>
Watchers are used to perform actions in response to data changes. They are particularly useful for asynchronous operations.
Example of Watchers:
<template>
<div>
<input v-model="question"/>
{{ answer }}
</div>
</template>
<script>
export default {
data() {
return {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
}
},
watch: {
question(newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...';
this.getAnswer();
}
},
methods: {
getAnswer() {
setTimeout(() => {
this.answer = 'The answer to your question is 42!';
}, 1000);
}
}
}
</script>
Vue Router is the official router for Vue.js, making it easy to map components to routes and manage navigation.
Example of Vue Router:
import Vue from 'vue';
import Router from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';
Vue.use(Router);
const router = new Router({
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
});
new Vue({
router,
render: h => h(App)
}).$mount('#app');
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
Example of Vuex:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
new Vue({
store,
render: h => h(App)
}).$mount('#app');
Let's take a look at a minimal web app that uses Vue. This guide will walk you through setting up the app from scratch, including installing necessary dependencies, setting up project structure, configuring Babel and Webpack, and writing the actual Vue code.
To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.
npm --version
npm init
package.json
file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.At the end of this setup, your project will have the following structure:
│
├── src
│ ├── main.js
│ └── App.vue
│
├── dist
│ └── index.html
│
├── package.json
├── webpack.config.js
└── .babelrc
Transpilers are used to convert code between various programming languages or versions of the same programming language. If we use any modern JavaScript features or frameworks, we can use a transpiler to transform our code to an earlier version of JavaScript that is supported by all web browsers.
The transpiler used in this example is Babel. To install it, use the following commands:
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react
npm install --save-dev vue-loader vue-template-compiler
Create a .babelrc
file to configure Babel. This file, in JSON format, describes how to transpile JavaScript code:
{
"presets": ["@babel/preset-env"]
}
Webpack combines all NPM dependencies into a single bundle.js
that can run in a browser. Use the following command to install it:
npm install --save-dev webpack webpack-cli webpack-dev-server
A special file named webpack.config.js
is needed to configure Webpack. The following is an example of this file:
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: path.resolve(__dirname, './src/main.js'),
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new VueLoaderPlugin()
],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
devServer: {
static: path.resolve(__dirname, './dist'),
port: 8000,
hot: true
},
};
To use Vue, you need to install the vue
library. Use the following command to install it:
npm install --save vue
In the src/App.vue
file, include the following Vue template:
<template>
<div id="app">
<h1>Hello World</h1>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped="">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
In the src/main.js
file, include the following Vue script:
import Vue from 'vue';
import App from './App.vue';
new Vue({
render: h => h(App),
}).$mount('#app');
In addition to JavaScript, an HTML file is required to render the Vue code. Place the index.html
file in the dist
folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="ie=edge" http-equiv="X-UA-Compatible"/>
<title>Minimal Vue App</title>
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
scripts
section of your package.json
file:
"scripts": {
"start": "webpack serve --mode development"
}
npm start
By following these steps, you will have set up a minimal Vue web application with Babel and Webpack.
Angular is a platform and framework for building single-page client applications using HTML and TypeScript. Angular is developed and maintained by Google, providing a robust and complete solution for web application development.
Pros:
Cons:
Components are the fundamental building blocks of an Angular application. Each component consists of an HTML template, a CSS stylesheet, and a TypeScript class.
Example of an Angular Component:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Hello World';
}
<!-- app.component.html -->
<div>
<h1>{{ title }}</h1>
This is a paragraph
</div>
Modules are containers for a cohesive block of code dedicated to an application domain, a workflow, or a set of related capabilities. Every Angular application has at least one module, the root module.
Example of an Angular Module:
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Directives are classes that add additional behavior to elements in your Angular applications. There are three kinds of directives in Angular: components, structural directives, and attribute directives.
Example of a Structural Directive (ngIf):
<div *ngif="isVisible">
This is visible.
</div>
Example of an Attribute Directive:
import { Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef, renderer: Renderer2) {
renderer.setStyle(el.nativeElement, 'backgroundColor', 'yellow');
}
}
Services in Angular are used to share data or logic across multiple components. Dependency Injection (DI) is a design pattern used to implement IoC, allowing a class to receive its dependencies from an external source.
Example of a Service:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class DataService {
private data: string[] = ['Data 1', 'Data 2', 'Data 3'];
getData() {
return this.data;
}
}
Injecting a Service into a Component:
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-data',
template:
<ul>
<li *ngfor="let item of data">{{ item }}</li>
</ul>
})
export class DataComponent implements OnInit {
data: string[];
constructor(private dataService: DataService) {}
ngOnInit() {
this.data = this.dataService.getData();
}
}
Angular Router enables navigation from one view to the next as users perform application tasks. It is a separate module in Angular that needs to be imported.
Example of Configuring Routes:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Using Router Outlet:
<!-- app.component.html -->
<nav>
<a routerlink="/">Home</a>
<a routerlink="/about">About</a>
</nav>
<router-outlet></router-outlet>
Angular provides two approaches to handling user input through forms: reactive forms and template-driven forms.
Example of Template-Driven Form:
<!-- app.component.html -->
<form #form="ngForm" (ngsubmit)="onSubmit(form)">
<input name="name" ngmodel="" required=""/>
<button type="submit">Submit</button>
</form>
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
onSubmit(form) {
console.log(form.value);
}
}
Example of Reactive Form:
// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports
ReactiveFormsModule
],
// other properties
})
export class AppModule { }
// app.component.ts
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
form = new FormGroup({
name: new FormControl('')
});
onSubmit() {
console.log(this.form.value);
}
}
<!-- app.component.html -->
<form (ngsubmit)="onSubmit()" [formgroup]="form">
<input formcontrolname="name"/>
<button type="submit">Submit</button>
</form>
The Angular CLI is a powerful tool that provides commands to scaffold, build, and manage Angular applications.
Creating a New Angular Application:
ng new my-angular-app
Serving the Application:
cd my-angular-app
ng serve
Generating Components, Services, etc.:
ng generate component my-component
ng generate service my-service
Angular's HttpClient
service allows you to communicate with backend services over the HTTP protocol.
Example of Using HttpClient:
// app.module.ts
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
// other imports
HttpClientModule
],
// other properties
})
export class AppModule { }
// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(private http: HttpClient) {}
getData() {
return this.http.get('https://api.example.com/data');
}
}
// data.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-data',
template:
<ul>
<li *ngfor="let item of data">{{ item }}</li>
</ul>
})
export class DataComponent implements OnInit {
data: any;
constructor(private dataService: DataService) {}
ngOnInit() {
this.dataService.getData().subscribe(data => {
this.data = data;
});
}
}
Angular Material is a UI component library for Angular developers. It follows Google's Material Design guidelines and provides a rich set of UI components.
Example of Using Angular Material:
ng add @angular/material
// app.module.ts
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
@NgModule({
imports: [
// other imports
MatButtonModule,
MatInputModule
],
// other properties
})
export class AppModule { }
<!-- app.component.html -->
<button mat-raised-button="">Click me</button>
<input matinput="" placeholder="Enter text"/>
Custom directives in Angular allow you to extend the functionality of HTML elements.
Example of a Custom Directive:
import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter') onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'white');
}
}
<!-- app.component.html -->
<p apphighlight="">Hover over me!
Pipes are a way to transform data in templates. Angular provides several built-in pipes, and you can also create your own custom pipes.
Example of a Custom Pipe:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}
<!-- app.component.html -->
{{ 2 | exponentialStrength: '10' }}
Lazy loading is a technique in Angular to load modules only when they are needed. This can significantly improve the performance of your application.
Example of Lazy Loading:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'feature',
loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
// feature-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { FeatureComponent } from './feature.component';
const routes: Routes = [
{ path: '', component: FeatureComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class FeatureRoutingModule { }
Angular provides robust tools for testing your applications, including unit tests and end-to-end tests.
Example of Unit Testing with Jasmine and Karma:
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
Example of End-to-End Testing with Protractor:
// e2e/src/app.e2e-spec.ts
import { browser, by, element } from 'protractor';
describe('workspace-project App', () => {
it('should display welcome message', () => {
browser.get('/');
expect(element(by.css('app-root h1')).getText()).toEqual('Welcome to app!');
});
});
Angular provides a module for adding animations to your applications. The @angular/animations
package offers a DSL (Domain Specific Language) to define and run animations.
Example of Angular Animations:
// app.module.ts
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
imports: [
// other imports
BrowserAnimationsModule
],
// other properties
})
export class AppModule { }
// app.component.ts
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
animations: [
trigger('openClose', [
state('open', style({
height: '200px',
opacity: 1,
backgroundColor: 'yellow'
})),
state('closed', style({
height: '100px',
opacity: 0.5,
backgroundColor: 'green'
})),
transition('open => closed', [
animate('0.5s')
]),
transition('closed => open', [
animate('0.5s')
]),
]),
],
})
export class AppComponent {
isOpen = true;
toggle() {
this.isOpen = !this.isOpen;
}
}
<!-- app.component.html -->
<div [@openclose]="isOpen ? 'open' : 'closed'" class="box">
<button (click)="toggle()">Toggle</button>
</div>
Angular Universal allows you to perform server-side rendering (SSR) of your Angular application. This can improve performance, SEO, and user experience.
Setting Up Angular Universal:
ng add @nguniversal/express-engine
Example of Angular Universal Application:
After running the command above, Angular CLI will automatically set up an Angular Universal application. You can build and serve your application with SSR using the following commands:
npm run build:ssr
npm run serve:ssr
Let's take a look at a minimal web app that uses Angular. This guide will walk you through setting up the app from scratch, including installing necessary dependencies, setting up project structure, configuring TypeScript and Webpack, and writing the actual Angular code.
To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.
npm --version
npm init
package.json
file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.At the end of this setup, your project will have the following structure:
│
├── src
│ ├── app
│ │ ├── app.component.ts
│ │ ├── app.component.html
│ │ ├── app.component.css
│ │ └── app.module.ts
│ └── main.ts
│
├── dist
│ └── index.html
│
├── package.json
├── tsconfig.json
└── webpack.config.js
To use Angular, you need to install the Angular core library and its dependencies. Use the following command to install them:
npm install --save @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic @angular/router rxjs zone.js
Angular is built using TypeScript, a statically typed superset of JavaScript. To install TypeScript and configure it, use the following command:
npm install --save-dev typescript ts-loader
Create a tsconfig.json
file to configure TypeScript. This file, in JSON format, describes how to compile TypeScript code:
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["es2017", "dom"],
"outDir": "./dist",
"baseUrl": "./",
"paths": {
"@app/*": ["src/app/*"]
}
},
"exclude": ["node_modules"]
}
Webpack combines all NPM dependencies into a single bundle.js
that can run in a browser. Use the following command to install it:
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin
A special file named webpack.config.js
is needed to configure Webpack. The following is an example of this file:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, './src/main.ts'),
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.html$/,
use: 'html-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/index.html')
})
],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
devServer: {
static: path.resolve(__dirname, './dist'),
port: 8000,
hot: true
},
};
Create an Angular component in the src/app/app.component.ts
file:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Hello World';
}
Create the HTML template for the component in src/app/app.component.html
:
<h1>{{ title }}</h1>
Create the CSS for the component in src/app/app.component.css
:
h1 {
color: #2c3e50;
font-family: Arial, sans-serif;
text-align: center;
margin-top: 50px;
}
Create the main module for the application in src/app/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 { }
Create the main entry file for the application in src/main.ts
:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Create the HTML file for the application in src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="ie=edge" http-equiv="X-UA-Compatible"/>
<title>Minimal Angular App</title>
</head>
<body>
<app-root></app-root>
<script src="bundle.js"></script>
</body>
</html>
scripts
section of your package.json
file:
"scripts": {
"start": "webpack serve --mode development"
}
npm start
By following these steps, you will have set up a minimal Angular web application with TypeScript and Webpack.
State management is crucial in front-end development, especially for applications with complex data flows and interactions. Various state management tools and libraries can help manage application state efficiently. Below, we'll cover some of the most popular state management tools: Redux, Vuex, MobX, NgRx, and Recoil.
Redux is a predictable state container for JavaScript apps. It is commonly used with React but can be used with any other JavaScript framework or library.
Pros:
Cons:
Core Concepts:
Example:
// actions.js
export const increment = () => ({ type: 'INCREMENT' });
export const decrement = () => ({ type: 'DECREMENT' });
// reducer.js
const initialState = { count: 0 };
export const counter = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
// store.js
import { createStore } from 'redux';
import { counter } from './reducer';
const store = createStore(counter);
// App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';
const App = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
Count: {count}
<button =="" onclick="{()"> dispatch(increment())}>+</button>
<button =="" onclick="{()"> dispatch(decrement())}>-</button>
</div>
);
};
export default App;
Vuex is the state management pattern and library for Vue.js applications, designed to manage the state of the entire application.
Pros:
Cons:
Core Concepts:
Example:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
},
actions: {
increment({ commit }) {
commit('increment');
},
decrement({ commit }) {
commit('decrement');
}
},
getters: {
count: state => state.count
}
});
<!-- App.vue -->
<template>
<div>
Count: {{ count }}
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
computed: {
...mapGetters(['count'])
},
methods: {
...mapActions(['increment', 'decrement'])
}
}
</script>
MobX is a library for managing state that makes state management simple and scalable by transparently applying functional reactive programming (TFRP).
Pros:
Cons:
Core Concepts:
Example:
// store.js
import { makeAutoObservable } from 'mobx';
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
export const counterStore = new CounterStore();
// App.js
import React from 'react';
import { observer } from 'mobx-react-lite';
import { counterStore } from './store';
const App = observer(() => {
return (
<div>
Count: {counterStore.count}
<button =="" onclick="{()"> counterStore.increment()}>+</button>
<button =="" onclick="{()"> counterStore.decrement()}>-</button>
</div>
);
});
export default App;
NgRx is a reactive state management library for Angular applications, inspired by Redux.
Pros:
Cons:
Core Concepts:
Example:
// actions.ts
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
// reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement } from './actions';
export const initialState = 0;
const _counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1)
);
export function counterReducer(state, action) {
return _counterReducer(state, action);
}
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './reducer';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
StoreModule.forRoot({ count: counterReducer })
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement } from './actions';
@Component({
selector: 'app-root',
template:
<div>
Count: {{ count$ | async }}
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
</div>
})
export class AppComponent {
count$: Observable<number>;
constructor(private store: Store<{ count: number }>) {
this.count$ = store.select('count');
}
increment() {
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
}
Recoil is a state management library for React that provides a minimal and flexible API for managing state.
Pros:
Cons:
Core Concepts:
Example:
// atoms.js
import { atom } from 'recoil';
export const countState = atom({
key: 'countState',
default: 0
});
// App.js
import React from 'react';
import { RecoilRoot, useRecoilState } from 'recoil';
import { countState } from './atoms';
const Counter = () => {
const [count, setCount] = useRecoilState(countState);
return (
<div>
Count: {count}
<button =="" onclick="{()"> setCount(count + 1)}>+</button>
<button =="" onclick="{()"> setCount(count - 1)}>-</button>
</div>
);
};
const App = () => (
<recoilroot>
<counter></counter>
</recoilroot>
);
export default App;
useEffect
in React or onMounted
in Vue) to manage side effects efficiently, like API calls or subscriptions.React.lazy
or Vue’s defineAsyncComponent
) to load only what’s needed, improving initial load times.useMemo
and useCallback
, to optimize performance by caching the results of expensive computations.React.memo
or Vue’s v-once
directive.react-window
or Vue’s vue-virtual-scroller
, which render only visible items for better performance.beforeEnter
in Vue Router or checking conditions in React Router) for authentication and authorization, enhancing security and user experience.useEffect
or Vue’s watch
, to handle data fetching and side effects efficiently.