Last modified: October 11, 2024

This article is written in: 🇺🇸

JavaScript Frameworks

What are frameworks?

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:

Problems addressed by the frameworks

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.

Disadvantages

As with everything in life, there are benefits and costs to using frameworks.

What kinds of frameworks are there?

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:

How to learn a framework?

  1. I recommend first learning the basics of Vanilla JavaScript and working on a few projects in it.
  2. Then, you may try to take a crash course on the framework of your choice to obtain a rough sense of what it is and what it can be used for.
  3. Then, immediately begin converting one of your previous projects to your preferred framework.

React

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:

Core Elements

Component

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

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

Mounting is the process of rendering a React component to the DOM. It involves several lifecycle methods:

  1. constructor(props) - Called before the component is mounted.
  2. render() - Defines what the component UI looks like.
  3. componentDidMount() - Called after the component is mounted.
  4. componentWillUnmount() - Called before the component is unmounted and destroyed.
  5. componentDidCatch() - Called when an error is thrown during rendering.

State

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>
    );
  }
}

DOM Events

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>
    );
  }
}

Virtual DOM

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:

  1. The entire virtual DOM is updated.
  2. The virtual DOM is compared to its previous state.
  3. Only the changed elements are updated in the real DOM.
  4. The changes in the real DOM trigger the actual rendering on the screen.

This process can lead to performance improvements, especially for applications with frequent updates.

Conditional Rendering

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

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.

useState

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>
  );
}

useEffect

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>
  );
}

useRef

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>
  );
}

useReducer

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>
  );
}

useContext

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>
  );
}

useMemo

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>
  );
}

Context API

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

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>
  );
}

Higher-Order Components (HOCs)

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

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

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

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

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

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

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 and the useImperativeHandle Hook

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>
  );
}

Minimal Web App Using React

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.

Prerequisites

To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.

  1. Download and install Node.js by selecting the right installer for your platform. It includes the NPM package manager.
  2. Verify that NPM was installed correctly by running the following command in your terminal:

npm --version

  1. Create an empty NPM package by initializing a new project:

npm init

  1. This will create a package.json file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.

Project Structure

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

Transpiler

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"]
}

Module Bundler

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
    },
};

Installing React

Two libraries are needed to use React: react and react-dom. Use the following command to install them:

npm install --save react react-dom

The Actual React Code

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>

Usage

  1. To use the app, first, start the development server. Add the following script to the scripts section of your package.json file:

"scripts": {
    "start": "webpack serve --mode development"
}

  1. Now, start the server by invoking the following command in your terminal:

npm start

  1. Open a web browser and navigate to http://localhost:8000/ to see the React app in action.

By following these steps, you will have set up a minimal React web application with Babel and Webpack.

Vue

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 and Cons

Pros:

Cons:

Core Elements

Component

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

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

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

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

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>

Lifecycle Hooks

Vue components have a series of lifecycle hooks that allow you to add code at specific stages.

  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeDestroy
  8. 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

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

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

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');

Minimal Web App Using Vue

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.

Prerequisites

To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.

  1. Download and install Node.js by selecting the right installer for your platform. It includes the NPM package manager.
  2. Verify that NPM was installed correctly by running the following command in your terminal:

npm --version

  1. Create an empty NPM package by initializing a new project:

npm init

  1. This will create a package.json file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.

Project Structure

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

Transpiler

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"]
}

Module Bundler

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
    },
};

Installing Vue

To use Vue, you need to install the vue library. Use the following command to install it:

npm install --save vue

The Actual Vue Code

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>

Usage

  1. To use the app, first, start the development server. Add the following script to the scripts section of your package.json file:

"scripts": {
    "start": "webpack serve --mode development"
}

  1. Now, start the server by invoking the following command in your terminal:

npm start

  1. Open a web browser and navigate to http://localhost:8000/ to see the Vue app in action.

By following these steps, you will have set up a minimal Vue web application with Babel and Webpack.

Angular

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 and Cons

Pros:

Cons:

Core Elements

Component

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

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

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 and Dependency Injection

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();
  }
}

Routing

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>

Forms

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>

Angular CLI

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

HttpClient

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

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

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

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

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 { }

Testing

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 Animations

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 (Server-Side Rendering)

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

Minimal Web App Using Angular

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.

Prerequisites

To install any dependency, a package manager is needed. The most common one is NPM, which comes with Node.js.

  1. Download and install Node.js by selecting the right installer for your platform. It includes the NPM package manager.
  2. Verify that NPM was installed correctly by running the following command in your terminal:

npm --version

  1. Create an empty NPM package by initializing a new project:

npm init

  1. This will create a package.json file, which is a build configuration file that defines all dependencies and scripts for creating and testing the project.

Project Structure

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

Installing Angular

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

Setting Up TypeScript

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"]
}

Module Bundler

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
  },
};

The Actual Angular Code

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>

Usage

  1. To use the app, first, start the development server. Add the following script to the scripts section of your package.json file:

"scripts": {
    "start": "webpack serve --mode development"
}

  1. Now, start the server by invoking the following command in your terminal:

npm start

  1. Open a web browser and navigate to http://localhost:8000/ to see the Angular app in action.

By following these steps, you will have set up a minimal Angular web application with TypeScript and Webpack.

State Management Tools

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

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

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

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

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

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;

Best Practices

General Guidelines

State Management

Components and Reusability

Performance Optimization

Routing and Navigation

Asynchronous Data Handling

Forms and Input Handling

Security

Testing and Debugging

Documentation and Code Quality

General Resources

React

Vue.js

Angular

Redux

Vuex

MobX

NgRx

Recoil

Table of Contents

  1. JavaScript Frameworks
  2. What are frameworks?
    1. Problems addressed by the frameworks
    2. Disadvantages
    3. What kinds of frameworks are there?
    4. How to learn a framework?
  3. React
    1. Core Elements
      1. Component
      2. Props
      3. Mounting
      4. State
      5. DOM Events
      6. Virtual DOM
      7. Conditional Rendering
    2. Hooks
      1. useState
      2. useEffect
      3. useRef
      4. useReducer
      5. useContext
      6. useMemo
      7. Context API
      8. Error Boundaries
      9. Higher-Order Components (HOCs)
      10. Render Props
      11. Portals
      12. Fragments
      13. React.memo
      14. Concurrent Mode
      15. Suspense
      16. Refs and the useImperativeHandle Hook
    3. Minimal Web App Using React
      1. Prerequisites
      2. Project Structure
      3. Transpiler
      4. Module Bundler
      5. Installing React
      6. The Actual React Code
      7. Usage
  4. Vue
    1. Pros and Cons
    2. Core Elements
      1. Component
      2. Props
      3. Directives
      4. Computed Properties
      5. Methods
      6. Lifecycle Hooks
      7. Watchers
    3. Vue Router
    4. Vuex
    5. Minimal Web App Using Vue
      1. Prerequisites
      2. Project Structure
      3. Transpiler
      4. Module Bundler
      5. Installing Vue
      6. The Actual Vue Code
      7. Usage
  5. Angular
    1. Pros and Cons
    2. Core Elements
      1. Component
      2. Modules
      3. Directives
      4. Services and Dependency Injection
      5. Routing
      6. Forms
    3. Angular CLI
    4. HttpClient
      1. Angular Material
      2. Custom Directives
      3. Pipes
      4. Lazy Loading
      5. Testing
      6. Angular Animations
      7. Angular Universal (Server-Side Rendering)
    5. Minimal Web App Using Angular
      1. Prerequisites
      2. Project Structure
      3. Installing Angular
      4. Setting Up TypeScript
      5. Module Bundler
      6. The Actual Angular Code
      7. Usage
  6. State Management Tools
    1. Redux
    2. Vuex
    3. MobX
    4. NgRx
    5. Recoil
    6. Best Practices
      1. General Guidelines
      2. State Management
      3. Components and Reusability
      4. Performance Optimization
      5. Routing and Navigation
      6. Asynchronous Data Handling
      7. Forms and Input Handling
      8. Security
      9. Testing and Debugging
      10. Documentation and Code Quality
  7. Links
    1. General Resources
    2. React
    3. Vue.js
    4. Angular
    5. Redux
    6. Vuex
    7. MobX
    8. NgRx
    9. Recoil