Variable Shadowing in JavaScript.

When learning JavaScript, you may come across confusing cases where a variable declared inside a block or function seems to “hide” another variable with the same name from an outer scope. This phenomenon is known as Variable Shadowing.

In this article, we’ll explore what variable shadowing is, why it happens, and how to avoid potential bugs caused by it with clear examples and explanations.

What is Variable Shadowing?

Variable shadowing in JavaScript is a phenomenon where a variable declared in an inner scope (such as inside a function or a code block defined by {}) has the same name as a variable in an outer scope.

The inner variable effectively "shadows" or hides the outer variable within the boundaries of its scope. It means, while inside the inner scope, you can only access the inner variable, not the outer one.

Here is a breakdown of the key characteristics:

  • Scope Priority: When JavaScript tries to resolve the variable name inside the inner scope, it finds the local declaration first and uses it.
  • Isolation: The two variables (inner and outer) are distinct and separate. Modifying the shadowed (inner) variable does not affect the value of the outer variable.
  • Mechanism: Shadowing relies on using a variable declaration keyword (var, let, or const) in the inner scope to create a new, local variable with the conflicting name.
JavaScript Example Code:

let theme = "light"; // Outer variable

function changeTheme() {
  let theme = "dark"; // Inner variable (shadows the outer 'theme')
  console.log("Inside function:", theme); // Output: dark
}

changeTheme();
console.log("Outside function:", theme); // Output: light
// The outer 'theme' was never changed; it was only hidden inside the function.

Types of Variable Shadowing in JavaScript.

1. Legal Shadowing: Legal shadowing happens when a variable declared in an inner scope (block or function) has the same name as a variable in the outer scope, and both follow proper scoping rules. It’s allowed as long as the inner and outer variables are declared using block-scoped keywords (let or const) or are defined in different scopes.

JavaScript Example Code:
let x = 10;

function demo() {
  let x = 20; // ✅ Legal shadowing
  console.log(x); // 20
}

demo();
console.log(x); // 10

Explanation: Here, both variables x are valid; one belongs to the global scope and the other to the function scope. They don’t interfere with each other.

2. Illegal Shadowing: Illegal shadowing occurs when a variable declared with var tries to shadow a variable declared with let or const in the same or overlapping scope. This leads to a SyntaxError because var is function-scoped, not block-scoped.

JavaScript Example Code:
let y = 10;

function test() {
  var y = 20; // ❌ Illegal shadowing
  console.log(y);
}

test();
SyntaxError: Identifier 'y' has already been declared.

Explanation:
  • The outer y is block-scoped (declared with let).
  • The inner y (declared with var) belongs to the same function scope.
  • JavaScript doesn’t allow this kind of redeclaration; hence, an error occurs.

Shadowing vs Hiding.

Though often used interchangeably, they can be slightly different in meaning in some languages.
  • Shadowing: When a local variable (in an inner scope) has the same name as a variable in an outer scope, it makes the outer one inaccessible in that scope. You can’t access the outer variable directly when shadowed.
  • Hiding: When a member of a subclass (like a method or variable) has the same name as one in its superclass, it hides the inherited member. You can still access the hidden member using super or the base class name.

🚫 How to Avoid Problems with Shadowing

Variable shadowing can easily make your code confusing and lead to unexpected behavior. While it’s not always harmful, it’s best to follow a few clean coding habits to prevent subtle bugs.

1. Use Clear and Descriptive Variable Names.

Many shadowing issues happen because developers reuse generic names like data, value, or temp in multiple places. Instead, use meaningful names that describe the variable’s purpose. For example, use userName inside a function rather than reusing the global variable name. This makes your code easier to understand and avoids accidental overwriting.

2. Prefer let and const over var.

The var keyword creates function-scoped variables, which can unintentionally leak outside of blocks and overlap with outer variables. By using let and const, you ensure block-level scoping, which makes it clearer where a variable starts and ends, reducing the chances of accidental shadowing.

3. Keep Scopes Small and Focused.

If a function or block of code becomes too large, it’s easy to lose track of which variable belongs where. Break long functions into smaller ones so that each has its own limited scope. This makes it less likely to reuse variable names and accidentally shadow something important.

4. Use Linters to Catch Shadowing Early.

Tools like ESLint can automatically detect and warn you about variable shadowing in your code. By enabling rules such as no-shadow, you can prevent unintentional redeclarations before they become real bugs.

5. Avoid Reusing Outer Variables for Temporary Values.

Sometimes developers reuse a variable from an outer scope to store a temporary value inside a loop or function. Instead of doing this, declare a new variable. It keeps the original value safe and prevents confusion when reading or debugging the code later.

Final Thoughts.

Variable shadowing isn’t always bad; in fact, it’s a natural part of JavaScript’s scope system.
But it can easily lead to confusion and bugs if not understood properly. By understanding how shadowing works across different scopes and using let/const wisely, you can write cleaner and more predictable JavaScript code.

JavaScript Variables and Constant.

JavaScript variables are fundamental to programming, acting as containers for storing data values. Historically, var was the only way to declare variables. However, with the introduction of ECMAScript 2015 (ES6), let and const were introduced to address shortcomings of var, particularly concerning scoping and mutability. Understanding the differences between these three keywords is crucial for writing modern, maintainable, and error-free JavaScript code.

Before we start understanding JavaScript variables and constants, let us first understand one important term that we are going to use again and again in our further discussion.

What is Hoisting?

Hoisting is a fundamental JavaScript mechanism where variable and function declarations are conceptually moved to the top of their containing scope during the compilation phase, before code execution.

This means that regardless of where variables and functions are declared, they are processed first. However, the exact behavior of hoisting differs significantly between var, let/const, and functions.

JavaScript Variables and Constant

The Legacy Keyword: var

The var keyword is the oldest way to declare a variable in JavaScript.

Scope of var.

Variables declared with var are function-scoped or globally-scoped. This means a var declared inside a function is only available within that function. If declared outside any function, it is global. Crucially, var ignores block scope (blocks defined by curly braces {}, such as in if statements or for loops).

JavaScript Example Code:

function exampleVarScope() {
  if (true) {
    var x = 10;
  }
  console.log(x); // Output: 10 (var ignores block scope)
}
// console.log(x); // Throws ReferenceError: x is not defined (x is function-scoped)

Hoisting of var

Variables declared with var are hoisted to the top of their scope (function or global). However, only the declaration is hoisted, not the initialization. This leads to a behavior where you can use a var before it is declared in the code, but its value will be undefined.

JavaScript Example of var Hoisting.

console.log(a); // Output: undefined
var a = 5;
console.log(a); // Output: 5

The Modern Standard: let

The let keyword was introduced in ES6 to provide better variable declaration management, primarily by introducing block scoping.

Scope of let

Variables declared with let are block-scoped. A block is any section of code enclosed in curly braces {} (e.g., if blocks, for loops, or just a standalone block). The variable is only accessible within that block and any nested sub-blocks.

JavaScript Example Code:
function exampleLetScope() {
  if (true) {
    let y = 20;
    console.log(y); // Output: 20
  }
  // console.log(y); // Throws ReferenceError: y is not defined (y is block-scoped)
}

Hoisting of let

let is also hoisted, but unlike var, it is not initialized. If you try to access a let variable before its declaration, JavaScript throws a ReferenceError. The time between the start of the scope and the declaration is called the Temporal Dead Zone (TDZ).

Example of let and TDZ:
// console.log(b); // ReferenceError: Cannot access 'b' before initialization (TDZ in effect)
let b = 15;
console.log(b); // Output: 15

Variables declared with let can be reassigned a new value.
let count = 1;
count = 2; // Allowed
console.log(count); // Output: 2

The Constant Keyword: const

The const keyword, also introduced in ES6, is used for declaring constant variables whose value is intended to remain unchanged.

Scope and Hoisting of const

Like let, const is block-scoped and is also subject to the Temporal Dead Zone (TDZ). This means attempting to use a const before its declaration results in a ReferenceError. Variables declared with const must be initialized at the time of declaration and cannot be reassigned later.

JavaScript Example Code:
const PI = 3.14159;
// PI = 3.14; // Throws TypeError: Assignment to constant variable.

Important Note on Objects/Arrays: While a const variable itself cannot be reassigned to a different value, if the value is an object or array, the contents of that object or array can be mutated.
const user = { name: "Alice" };
user.name = "Bob"; // Allowed: Object property modification
console.log(user.name); // Output: Bob

const numbers = [1, 2];
numbers.push(3); // Allowed: Array mutation
// numbers = [4, 5]; // NOT Allowed: Reassignment of the const variable

Modern Best Practice: In modern JavaScript, it's generally recommended to:
  • Use const by default for any variable that won't be reassigned. This prevents accidental changes and makes the code's intent clearer.
  • Use let for variables that need to be reassigned (e.g., loop counters, variables holding a state that changes).
  • Avoid using var completely due to its confusing scope and hoisting behavior.

How To Check, Install and Update Angular on Your System.

Angular, powered by the Angular CLI (Command Line Interface), is a robust platform for building scalable web applications. The CLI is your essential tool, allowing you to scaffold, develop, test, and deploy your Angular projects right from your command shell. As an Angular Expert, I'll guide you through the crucial steps of managing the Angular CLI on your system: checking the current version, installing it for the first time, and keeping it up-to-date.

Prerequisites: The Node.js and npm Foundation

Before you can work with the Angular CLI, you must have Node.js and its package manager, npm (Node Package Manager), installed on your system. Angular CLI is an npm package and uses Node.js to run its tooling outside the browser.

Check Your Node.js and npm Versions

Open your terminal or command prompt and run the following commands:

Step 1: Check Node.js version:

node -v

Step 2: Check npm version:
npm -v

Angular typically requires an active LTS (Long Term Support) or maintenance LTS version of Node.js.
If you don't have Node.js or if your version is outdated, visit the official Node.js website to download and install the latest LTS version.

How to Check Your Angular CLI Version.

Knowing your current Angular CLI version is the first step to ensuring you are working with the expected features and compatibility.

Check Global Angular CLI Version.

The most straightforward way to check your globally installed Angular CLI version is to use the ng version command in your terminal.
ng version
# or
ng v

Example Output:
Angular CLI Version

The output will display the versions of the Angular CLI, Node.js, and several key Angular packages (if run inside an Angular project, it will show project-specific versions as well).

Check Project-Specific Angular Version

If you are inside an Angular project folder, running ng version will give you a detailed breakdown of the Angular packages installed locally in that project (e.g., @angular/core, @angular/common, etc.). This is important because a project's local dependencies can sometimes differ from your global CLI version. You can also inspect the package.json file in your project root to see all your Angular dependency versions.

How To Install the Angular CLI.

If you've never installed the Angular CLI before, you'll need to install it globally using npm. This makes the ng command available from any location in your command line.

Execute the following command in your terminal:
npm install -g @angular/cli
  • npm install: The command to install npm packages.
  • -g: The flag for installing the package globally.
  • @angular/cli: The name of the Angular CLI package.

While generally not recommended unless you have a specific requirement, you can install a particular major version of the CLI by appending the version number:
npm install -g @angular/cli@17
# This will install the latest available version in the Angular v17 line

After the installation completes, run the check command again to verify that the latest CLI has been successfully installed:
ng version

How To Update Angular (CLI and Project).

The Angular team frequently releases new versions, often including performance improvements, new features, and bug fixes. Keeping both your global Angular CLI and your Angular project packages up-to-date is best practice.

To get the latest global version of the CLI, simply re-run the install command, optionally specifying the @latest tag:
npm install -g @angular/cli@latest
This command will uninstall the old global version and install the most recent stable release.

Note: While updating you might get a warning message show below. We usualy get this warning when your Node.Js version is not matching with latest required of Angular. You need to install the updated version of Node.Js in your system to fix this error.

Angular Updating

Update Local Project Packages.

Updating your Angular project itself is slightly more complex, especially when jumping between major versions (e.g., from Angular 16 to Angular 17). Angular provides a powerful command, ng update, designed to automate this process.

1. Check for Available Updates: The first step is to see what updates are available for your current project:
ng update

This command lists packages that have available updates, along with a recommendation on which packages to update first.

2. Run the Primary Update: For major versions, it is recommended to update the core packages (@angular/core and @angular/cli) first:
ng update @angular/core @angular/cli

The ng update command is smart: it updates packages and runs migration schematics to automatically update your code to match the new APIs and conventions, minimizing manual effort.

3. Update Other Dependencies: After updating the core packages, you may need to update other libraries and tools, such as Angular Material or other third-party libraries:
ng update @angular/material

Pro Tip: For a detailed, step-by-step guide tailored to your specific current and target versions, always consult the official Angular Update Guide on the Angular website. It provides customized instructions and highlights manual changes that may be necessary, especially for major version upgrades (e.g., from v15 to v17).

Conclusion.

The Angular CLI is a cornerstone of the Angular development experience. By mastering the simple commands to check (ng version), install (npm install -g @angular/cli), and update (ng update), you ensure your development environment is modern, efficient, and ready to leverage the latest features Angular has to offer.

How To Resolve Circular Dependency Error in Angular.

In Angular, sometimes your code gets stuck, like two friends asking each other the same question but never answering. This problem is called a Circular Dependency. It happens when two (or more) parts of your code depend on each other in a loop, and Angular doesn’t know where to start.

Before understanding the process of resolving Circular Dependency, let's first understand what Dependency is and why it is so important in building any software application.

What is Dependency?

In programming, a dependency is a relationship where one piece of code relies on another to function correctly. This is a fundamental concept in software development, as modern applications are rarely built from scratch. Instead, developers reuse existing, pre-written code in the form of libraries, frameworks, or modules.

Dependency Example:

Think of baking a cake. To make the cake (your code), you need flour, sugar, and eggs (dependencies). You don't have to create the flour or sugar yourself; you buy them from a store (like downloading from a library). Without these ingredients, you can't bake the cake. Your cake recipe depends on the ingredients.

In software, dependencies work the same way:
  • A component (like a button on a web page) might depend on a styling library to make it look good.
  • A service that handles user login might depend on a database library to interact with the database.

This dependency relationship is usually one-way. The button needs the styling library, but the styling library doesn't need to know anything about the button. The login service needs the database library, but the database library doesn't need to know anything about the login service.

When you have a dependency, if the code you're depending on changes or is unavailable, your code will likely fail. This is why managing dependencies is a crucial part of software development.

Understanding this one-way relationship is the key to grasping why a circular dependency, where two components depend on each other, is so problematic.

What is Circular Dependency?

A circular dependency occurs when two or more components, modules, or services depend on each other, creating a closed loop. This prevents them from being properly initialized or compiled because each one is waiting for the other to be ready first.

Imagine two people, Alice and Bob.
  • Alice says, "I can't start my project until Bob gives me his budget report."
  • Bob says, "I can't finish my budget report until Alice starts her project, so I can get the final numbers."

They are stuck in an eternal loop. Alice needs Bob's input to begin, and Bob needs Alice's progress to finish. Neither can move forward, and the project is deadlocked. This is a circular dependency.

In a software application, the same principle applies. For example, in a codebase:
  • UserService needs to call a method in PaymentService to process a transaction.
  • PaymentService needs to access a user's details, which it gets by calling a method in UserService.

This creates a tight coupling where UserService depends on PaymentService, and PaymentService depends on UserService. The system can't decide which service to initialize first, leading to errors. This anti-pattern indicates a poor design that makes the code difficult to maintain, test, and reuse.

Let's recreate circular dependency to understand the exact condition and how to resolve it.

Example of Circular Dependency in Angular.

user.service.ts
// user.service.ts
import { Injectable } from '@angular/core';
import { PaymentService } from './payment.service';

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private paymentService: PaymentService) {}

  getUserDetails(userId: number) {
    console.log("Fetching user details for", userId);

    // Oh no ❌ UserService is calling PaymentService
    this.paymentService.getPaymentsForUser(userId);
  }
}

payment.service.ts
// payment.service.ts
import { Injectable } from '@angular/core';
import { UserService } from './user.service';

@Injectable({ providedIn: 'root' })
export class PaymentService {
  constructor(private userService: UserService) {}

  getPaymentsForUser(userId: number) {
    console.log("Fetching payments for", userId);

    // Oh no ❌ PaymentService is calling UserService again
    this.userService.getUserDetails(userId);
  }
}

This Angular code is trying to create a userService, but userService needs PaymentService. Then Angular tries to create PaymentService, but PaymentService needs userService. Angular goes back to create userService...and this cycle never ends. This is a circular dependency.

userService -> PaymentService -> userService
Error: Circular dependency in DI detected for UserService

We need to break the direct cycle by introducing a shared service or event mediator. Let's learn both methods one by one.

Method 1: Resolve Circular Dependency Using Shared Service.

This is the most popular and easiest way to break circular dependency by moving the common logic into a third service that both can use.

Here are a few simple steps that you need to follow:
1. Identify the code that causes a circular reference. 
2. Create a new independent service. 
3. Inject this service into both classes instead of injecting each other.

Fix Example:
This is a mediator service that holds the shared logic.
// data.service.ts
@Injectable({ providedIn: 'root' })
export class DataService {
  getUserDetails(userId: string) {
    console.log("Fetching user details:", userId);
  }

  getPaymentsForUser(userId: string) {
    console.log("Fetching payments for user:", userId);
  }
}

This is the modified userService after introducing the shared service.
// user.service.ts
@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private data: DataService) {}

  getUserDetails(userId: string) {
    this.data.getUserDetails(userId);
  }
}

This is the modified PaymentService after introducing the shared service.
// payment.service.ts
@Injectable({ providedIn: 'root' })
export class PaymentService {
  constructor(private data: DataService) {}

  getPaymentsForUser(userId: string) {
    this.data.getPaymentsForUser(userId);
  }
}

Now both services depend only on DataService → no cycle. This is cleaner and easier to maintain.

Method 2: Use forwardRef(). 

If the dependency is truly unavoidable (say, PaymentService must call UserService directly), we can use forwardRef().

How does forwardRef() Work?

In Angular, when you create a service or class, Angular tries to resolve all dependencies immediately at runtime.
But in circular dependency cases, one class might not be defined yet when Angular tries to use it.

👉 forwardRef() is a helper function that tells Angular:

“Don’t try to resolve this dependency right now — wait until everything is defined, and then resolve it.”

Fix Example:

// user.service.ts
@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(
    @Inject(forwardRef(() => PaymentService)) private payment: PaymentService
  ) {}

  getUserDetails(userId: string) {
    console.log("Fetching user details:", userId);
    this.payment.getPaymentsForUser(userId);
  }
}

// payment.service.ts
@Injectable({ providedIn: 'root' })
export class PaymentService {
  constructor(
    @Inject(forwardRef(() => UserService)) private user: UserService
  ) {}

  getPaymentsForUser(userId: string) {
    console.log("Fetching payments for user:", userId);
    this.user.getUserDetails(userId);
  }
}

Now Angular delays resolution until both services are defined.
The circular error is avoided, but if you keep calling each other directly, there’s still a risk of infinite recursion, so use it carefully.

ASP.NET Core MVC Application for CURD Operation.

 


View Part

1. Create.cshtml

@model BookStorage.Models.Book
@{
    ViewData["Title"] = "Add Book";
}
<h2>Add Book</h2>

<form asp-action="Create" method="post">
    <div class="form-group">
        <label asp-for="Title"></label>
        <input asp-for="Title" class="form-control" />
        <span asp-validation-for="Title" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Author"></label>
        <input asp-for="Author" class="form-control" />
        <span asp-validation-for="Author" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="YearPublished"></label>
        <input asp-for="YearPublished" class="form-control" />
        <span asp-validation-for="YearPublished" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Price"></label>
        <input asp-for="Price" class="form-control" />
        <span asp-validation-for="Price" class="text-danger"></span>
    </div>
    <button type="submit" class="btn btn-success">Save</button>
</form>

2. Edit.cshtml

@model BookStorage.Models.Book
@{
    ViewData["Title"] = "Edit Book";
}
<h2>Edit Book</h2>
<form asp-action="Edit" method="post">
    <input type="hidden" asp-for="BookId" />

    <div class="form-group">
        <label asp-for="Title"></label>
        <input asp-for="Title" class="form-control" />
        <span asp-validation-for="Title" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="Author"></label>
        <input asp-for="Author" class="form-control" />
        <span asp-validation-for="Author" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="YearPublished"></label>
        <input asp-for="YearPublished" class="form-control" />
        <span asp-validation-for="YearPublished" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="Price"></label>
        <input asp-for="Price" class="form-control" />
        <span asp-validation-for="Price" class="text-danger"></span>
    </div>

    <button type="submit" class="btn btn-primary">Update</button>
    <a asp-action="Index" class="btn btn-secondary">Cancel</a>
</form>
@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

3. Index.cshtml

@model IEnumerable<BookStorage.Models.Book>
@{
    ViewData["Title"] = "Book List";
}
@if(TempData["SuccessMessage"] != null)
{
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        @TempData["SuccessMessage"]
        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
    </div>
}
<h1>@ViewData["Title"]</h1>
<p>
    <a asp-action="Create" class="btn btn-primary">Add New Book</a>
</p>
<table class="table table-bordered">
    <thead>
        <tr>
            <th>Title</th>
            <th>Author</th>
            <th>Published Year</th>
            <th>Price</th>
            <th></th>
        </tr>
    </thead>
     <tbody>
          @foreach (Book book in Model)
          {
              <tr>
                  <td>@book.Title</td>
                  <td>@book.Author</td>
                  <td>@book.YearPublished</td>
                  <td>@book.Price</td>
                  <td>
                      <a asp-action="Edit" asp-route-id="@book.BookId" class="btn btn-warning">Edit</a> |
                      <a asp-action="Details" asp-route-id="@book.BookId" class="btn btn-info">Details</a> |
                      <a asp-action="Delete" asp-route-id="@book.BookId" class="btn btn-danger">Delete</a>
                  </td>
              </tr>
          }
     </tbody>
</table>

Controller

1. BookController.cs

using BookStorage.Data;
using BookStorage.Models;
using Microsoft.AspNetCore.Mvc;

namespace BookStorage.Controllers
{
    public class BookController : Controller
    {
        private readonly AppDbContext _context;

        public BookController(AppDbContext context)
        {
            _context = context;
        }
        [HttpGet]
        [Route("books")]
        public IActionResult Index()
        {
            return View(_context.Books.ToList());
        }

        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(Book book)
        {
            if (ModelState.IsValid)
            {
                book.BookId = Guid.NewGuid();
                _context.Books.Add(book);
                _context.SaveChanges();
                TempData["SuccessMessage"] = "Book created successfully!";
                return RedirectToAction(nameof(Index));
            }
            return View(book);
        }
        [HttpGet]
        public IActionResult Edit(Guid id)
        {
            var book = _context.Books.Find(id);
            if (book == null) return NotFound();
            return View(book);
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(Guid id, Book book)
        {
            if (id != book.BookId) return NotFound();

            if (ModelState.IsValid)
            {
                _context.Update(book);
                _context.SaveChanges();
                return RedirectToAction(nameof(Index));
            }
            return View(book);
        }
        public IActionResult Delete(Guid id)
        {
            var book = _context.Books.Find(id);
            if (book == null) return NotFound();
            return View(book);
        }
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(Guid id)
        {
            var book = _context.Books.Find(id);
            if (book != null)
            {
                _context.Books.Remove(book);
                _context.SaveChanges();
            }
            return RedirectToAction(nameof(Index));
        }
        public IActionResult Details(Guid id)
        {
            var book = _context.Books.Find(id);
            if (book == null) return NotFound();
            return View(book);
        }
    }
}

Program.cs

using BookStorage.Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'BookStorageContext' not found.")));
var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

AppDbContext.cs

using BookStorage.Models;
using Microsoft.EntityFrameworkCore;

namespace BookStorage.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<Book> Books { get; set; }
    }
}

Types of Data Binding in Angular.

Data binding in Angular is a powerful feature that allows you to synchronize data between the component and the view. It is used to ensure that the user interface reflects the current state of the data model and vice versa. This synchronization helps in creating dynamic and interactive applications.

There are four main types of data binding in Angular:
  • Interpolation
  • Property Binding
  • Event Binding
  • Two-Way Binding

1. Interpolation

Why it's used: Interpolation allows you to display component data in the template. It is a simple way to bind data from the component to the view.

Example:
import { Component } from '@angular/core';

@Component({
  selector: 'app-interpolation',
  template: `<h1>{{ title }}</h1>`
})
export class InterpolationComponent {
  title: string = 'Hello, Angular!';
}

Use Case: Use interpolation when you want to display a string or a number from the component in the template.

2. Property Binding

Why it's used: Property binding allows you to bind component properties to the properties of HTML elements. This is useful for dynamically setting attributes or properties based on the component's state.

Example:
import { Component } from '@angular/core';

@Component({
  selector: 'app-property-binding',
  template: `<img [src]="imageUrl" alt="Image">`
})
export class PropertyBindingComponent {
  imageUrl: string = 'https://example.com/image.png';
}

Use Case: Use property binding when you need to set properties of HTML elements, such as src, disabled, or value, based on the component's data.

3. Event Binding

Why it's used: Event binding allows you to listen to events emitted by DOM elements and execute methods in the component in response. This is essential for handling user interactions.

Example:
import { Component } from '@angular/core';

@Component({
  selector: 'app-event-binding',
  template: `<button (click)="onClick()">Click Me!</button>`
})
export class EventBindingComponent {
  onClick() {
    alert('Button clicked!');
  }
}

Use Case: Use event binding when you want to respond to user actions, such as clicks, key presses, or mouse movements.

4. Two-Way Binding

Why it's used: Two-way binding allows for a two-way synchronization between the component and the view. It is particularly useful in forms where user input needs to be reflected in the component's properties and vice versa.

Example:
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-two-way-binding',
  template: `<input [(ngModel)]="name" placeholder="Enter your name">
             <p>Hello, {{ name }}!</p>`
})
export class TwoWayBindingComponent {
  name: string = '';
}

Use Case: Use two-way binding when you need to bind form inputs to component properties, allowing for real-time updates as the user types.

Angular Data Binding Example.

Below is a complete working example of an Angular application that demonstrates all four types of data binding: interpolation, property binding, event binding, and two-way binding.

Step 1: Set Up Angular Application
First, make sure you have Angular CLI installed. You can create a new Angular application using the following command:
ng new data-binding-example
cd data-binding-example

Step 2: Create a Component
Next, create a new component called data-binding:
ng generate component data-binding

Step 3: Update the Component
Now, open the data-binding.component.ts file and update it as follows:
import { Component } from '@angular/core';

@Component({
  selector: 'app-data-binding',
  templateUrl: './data-binding.component.html',
  styleUrls: ['./data-binding.component.css']
})
export class DataBindingComponent {
  title: string = 'Data Binding Example';
  imageUrl: string = 'https://via.placeholder.com/150';
  name: string = '';

  onClick() {
    alert(`Hello, ${this.name}!`);
  }
}

Step 4: Update the Template
Next, open the data-binding.component.html file and update it with the following code:
<h1>{{ title }}</h1> <!-- Interpolation -->

<img [src]="imageUrl" alt="Placeholder Image"> <!-- Property Binding -->

<button (click)="onClick()">Click Me!</button> <!-- Event Binding -->

<input [(ngModel)]="name" placeholder="Enter your name"> <!-- Two-Way Binding -->
<p>Hello, {{ name }}!</p> <!-- Interpolation -->

Step 5: Import FormsModule
To use two-way binding with ngModel, you need to import FormsModule. Open the app.module.ts file and update it as follows:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // Import FormsModule
import { AppComponent } from './app.component';
import { DataBindingComponent } from './data-binding/data-binding.component';

@NgModule({
  declarations: [
    AppComponent,
    DataBindingComponent
  ],
  imports: [
    BrowserModule,
    FormsModule // Add FormsModule to imports
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 6: Update the Main Template
Finally, open the app.component.html file and include the data-binding component:
<app-data-binding></app-data-binding>

Step 7: Run the Application
Now, you can run the application using the following command: ng serve

Open your browser and navigate to http://localhost:4200. You should see the following:
  • A title displayed using interpolation.
  • An image is displayed using property binding.
  • A button that shows an alert with a greeting when clicked (event binding).
  • An input field that updates the greeting in real-time using two-way binding.


Summary

  • Interpolation: Displays component data in the template.
  • Property Binding: Binds component properties to HTML element properties.
  • Event Binding: Listens to events and executes component methods.
  • Two-Way Binding: Synchronizes data between the component and the view, especially in forms.
These data binding techniques enable developers to create dynamic and responsive applications in Angular, enhancing user experience and interaction.

Difference Between ngOnInit and constructor in Angular.

In Angular, both the constructor and the ngOnInit lifecycle hook are used in components, but they serve different purposes and are called at different times in the component's lifecycle. Understanding the use and their difference will help you build a better Angular Application.

Constructor

The constructor is a TypeScript feature used to initialize class members. It is called when the component is instantiated. The constructor is called before Angular sets any input properties or runs any lifecycle hooks.

Usage: You typically use the constructor for dependency injection and to initialize class properties. However, you should avoid complex logic or operations that depend on the component's view or input properties.

ngOnInit

ngOnInit is a lifecycle hook provided by Angular that is called after the component's constructor and after Angular has initialized all data-bound properties. It is called once the component is fully initialized, and it is a good place to perform any additional initialization tasks that require access to input properties or the component's view.

Usage: You can use ngOnInit to fetch data from services, set up subscriptions, or perform any other initialization that requires the component to be fully set up.

Difference Between Constructor and ngOnInit.

Feature constructor() ngOnInit()
Purpose Initializes class members and injects dependencies Used for component initialization logic after inputs are set
Lifecycle Runs when the class is instantiated (before Angular binds inputs) Runs after the constructor, when Angular has fully initialized the component
Best Use Dependency injection, initializing simple properties Fetching data, calling APIs, and logic that depends on @Input()
Called By JavaScript engine Angular framework

Example Angular Code:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
})
export class ExampleComponent implements OnInit {
  data: string;

  constructor() {
    // Constructor logic (e.g., dependency injection)
    console.log('Constructor called');
  }

  ngOnInit() {
    // Initialization logic that requires the component to be fully set up
    this.data = 'Hello, World!';
    console.log('ngOnInit called');
  }
}

In this example, the constructor is used for basic setup, while ngOnInit is used to initialize the data property after the component is fully initialized.

Summary

  • Use a constructor for setting up dependencies and initializing properties.
  • Use ngOnInit for any logic that needs Angular to finish setting up the component (like input bindings or DOM access).

✅ Rule of Thumb: “Keep constructor light, move logic to ngOnInit().

DON'T MISS

Nature, Health, Fitness
© all rights reserved
made with by AlgoLesson