DEV Community

Cover image for Learn JS Frameworks With Me: Angular - A Beginner's Journey
Shaman Shetty
Shaman Shetty

Posted on β€’ Edited on

Learn JS Frameworks With Me: Angular - A Beginner's Journey

Hey there, fellow code enthusiasts! Welcome to the second installment of what I'm calling my "Learn JS Frameworks With Me" series. I'm absolutely thrilled to kick this off with you because, let's face it, learning frameworks can be intimidating - but it's so much better when we do it together!

The Framework Journey Begins

I gotta admit. When I first started working with JavaScript, I found the native DOM API frustrating. Selecting elements felt 'troublesome', event handling was inconsistent across browsers, and animations required writing dozens of lines of code. Sound familiar?

Back then, jQuery came to the rescue with its "Write less, do more" philosophy. It was revolutionary and transformed how an entire generation of developers (myself included) approached frontend development.

But today, we're venturing into new territory: Angular. It's a framework that's been around for a while, has enterprise-level backing from Google, and promises to solve many of the headaches we face with building modern web applications.

Why Angular in 2025?

You might be wondering: "With all these modern frameworks out there, why should I learn Angular?"

Great question! Here's why I think Angular deserves your attention:

  1. Enterprise Adoption: Angular is widely used in large-scale applications. During my consulting work, I've encountered numerous businesses running critical systems on Angular.

  2. Complete Solution: While some frameworks focus on the view layer, Angular provides a complete platform with everything from routing to form validation baked in.

  3. TypeScript Integration: Angular embraces TypeScript, which adds static typing to JavaScript. Trust me, once you get used to it, you'll wonder how you ever lived without it!

  4. Job Market Reality: Browse through job listings and you'll see Angular mentioned frequently, especially for enterprise-level positions.

  5. Opinionated Structure: Angular has opinions about how your app should be structured, which can be a blessing when working on large teams.

Setting Up Your First Angular Project

Let's get our hands dirty! I spent about an hour yesterday just getting my environment ready, and oh boy, was it a journey. But don't worry, I'll walk you through it step by step.

Prerequisites

Before we dive in, you'll need:

  • Node.js and npm: Angular requires Node.js version 14.x or later
  • Angular CLI: The command line interface for Angular

Step 1: Install Node.js and npm

If you don't already have Node.js installed, head over to nodejs.org and download the latest LTS version.

To verify your installation, open your terminal and run:

node -v
npm -v
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Angular CLI

Angular CLI makes it easy to create applications, generate code, and perform various development tasks. Install it globally using npm:

npm install -g @angular/cli
Enter fullscreen mode Exit fullscreen mode

Verify the installation:

ng version
Enter fullscreen mode Exit fullscreen mode

When I first ran this command and saw the elaborate ASCII art logo in my terminal, I literally said "Whoa!" out loud. It's the little things, right?

Step 3: Create Your First Angular Project

Now we're ready to create our first project:

ng new learn-angular-with-me
Enter fullscreen mode Exit fullscreen mode

During the setup, Angular CLI will ask:

  • Whether you want to add Angular routing (Yes!)
  • Which CSS preprocessor you want to use (I'm sticking with regular CSS for now)

This was the moment I realized Angular wasn't messing around. The CLI asks thoughtful questions and sets up a comprehensive project structure.

Step 4: Run Your Application

Navigate to your project directory:

cd learn-angular-with-me
Enter fullscreen mode Exit fullscreen mode

Start the development server:

ng serve --open
Enter fullscreen mode Exit fullscreen mode

The --open flag will automatically open your browser to http://localhost:4200/.

When I first saw that Angular logo spinning in my browser, I felt like I'd just joined some elite developer club. It was both exciting and slightly intimidating!

The Angular Project Structure

Coming from jQuery, Angular feels... comprehensive. And by comprehensive, I mean there's a LOT going on. My first reaction was honestly a bit of overwhelm when I opened the project structure:

learn-angular-with-me/
β”œβ”€β”€ node_modules/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ app.component.css
β”‚   β”‚   β”œβ”€β”€ app.component.html
β”‚   β”‚   β”œβ”€β”€ app.component.spec.ts
β”‚   β”‚   β”œβ”€β”€ app.component.ts
β”‚   β”‚   └── app.module.ts
β”‚   β”œβ”€β”€ assets/
β”‚   β”œβ”€β”€ environments/
β”‚   β”œβ”€β”€ favicon.ico
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ main.ts
β”‚   β”œβ”€β”€ polyfills.ts
β”‚   β”œβ”€β”€ styles.css
β”‚   └── test.ts
β”œβ”€β”€ angular.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── ... (more config files)
Enter fullscreen mode Exit fullscreen mode

Wait, what? Where do I even start? In jQuery, I'd just create an HTML file, link a JS file, and start coding. Here, I'm looking at something that feels more like an enterprise-level application structure.

Let's break down the key parts:

  • src/app/: This is where most of your application code will live
  • app.component.ts: The main component class
  • app.component.html: The HTML template for the main component
  • app.module.ts: The root module that tells Angular how to assemble your application

The Mental Shift: Component-Based Thinking

The biggest mental shift I've had to make is thinking in components. In jQuery, I was used to writing functions and manipulating the DOM directly. With Angular, everything revolves around components.

A component in Angular is basically a building block that contains:

  • An HTML template (the view)
  • A TypeScript class (the logic)
  • CSS styles (the look)

Let me show you what a basic component looks like:

// task.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-task',
  templateUrl: './task.component.html',
  styleUrls: ['./task.component.css']
})
export class TaskComponent {
  taskName: string = '';
  isCompleted: boolean = false;

  toggleComplete() {
    this.isCompleted = !this.isCompleted;
  }
}
Enter fullscreen mode Exit fullscreen mode
<!-- task.component.html -->
<div class="task" [class.completed]="isCompleted">
  <span>{{ taskName }}</span>
  <button (click)="toggleComplete()">Toggle</button>
</div>
Enter fullscreen mode Exit fullscreen mode
/* task.component.css */
.task {
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.completed {
  text-decoration: line-through;
  color: #888;
}
Enter fullscreen mode Exit fullscreen mode

This was my first "aha!" moment with Angular. Instead of having your HTML, JS, and CSS scattered across different files, they're bundled together based on functionality. It's like each component is its own mini-application!

Two-Way Data Binding: The Magic Begins

Remember how in jQuery, we had to manually update the DOM when data changed? That looked something like this:

$('#task-input').val('New Task');
// And then later:
const taskValue = $('#task-input').val();
Enter fullscreen mode Exit fullscreen mode

Angular introduces two-way data binding, which feels like magic when you first see it:

<input [(ngModel)]="taskName" placeholder="Enter task">
<p>Current task: {{ taskName }}</p>
Enter fullscreen mode Exit fullscreen mode

That [(ngModel)] syntax is called "banana in a box" (seriously, that's what Angular devs call it!), and it automatically keeps your component property and the input value in sync.

When I first saw this working, I literally said "Wait, that's it? Where's the rest of the code?" It felt too good to be true!

Building Our First Feature: A Task List

Let's build a simple task list component to get a feel for how Angular works in practice. First, we'll generate a new component:

ng generate component task-list
Enter fullscreen mode Exit fullscreen mode

Now, let's update our component files:

// task-list.component.ts
import { Component } from '@angular/core';

interface Task {
  id: number;
  text: string;
  completed: boolean;
}

@Component({
  selector: 'app-task-list',
  templateUrl: './task-list.component.html',
  styleUrls: ['./task-list.component.css']
})
export class TaskListComponent {
  newTaskText: string = '';
  tasks: Task[] = [];

  addTask() {
    if (this.newTaskText.trim()) {
      this.tasks.push({
        id: Date.now(),
        text: this.newTaskText,
        completed: false
      });
      this.newTaskText = '';
    }
  }

  toggleComplete(task: Task) {
    task.completed = !task.completed;
  }

  deleteTask(id: number) {
    this.tasks = this.tasks.filter(task => task.id !== id);
  }
}
Enter fullscreen mode Exit fullscreen mode
<!-- task-list.component.html -->
<div class="task-manager">
  <h2>Task List</h2>

  <div class="add-task">
    <input [(ngModel)]="newTaskText" placeholder="Add a new task..." (keyup.enter)="addTask()">
    <button (click)="addTask()">Add</button>
  </div>

  <ul class="task-list">
    <li *ngFor="let task of tasks" [class.completed]="task.completed">
      <span (click)="toggleComplete(task)">{{ task.text }}</span>
      <button (click)="deleteTask(task.id)">Delete</button>
    </li>
  </ul>
</div>
Enter fullscreen mode Exit fullscreen mode
/* task-list.component.css */
.task-manager {
  max-width: 500px;
  margin: 0 auto;
}

.add-task {
  display: flex;
  margin-bottom: 20px;
}

.add-task input {
  flex-grow: 1;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px 0 0 4px;
}

.add-task button {
  padding: 8px 16px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
}

.task-list {
  list-style-type: none;
  padding: 0;
}

.task-list li {
  padding: 10px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.task-list li.completed span {
  text-decoration: line-through;
  color: #888;
}

.task-list li button {
  background-color: #f44336;
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

And don't forget to update the app.module.ts file to include the FormsModule (needed for ngModel):

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

import { AppComponent } from './app.component';
import { TaskListComponent } from './task-list/task-list.component';

@NgModule({
  declarations: [
    AppComponent,
    TaskListComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

Finally, let's use our new component in the app.component.html:

<!-- app.component.html -->
<div class="container">
  <h1>My Angular Task Manager</h1>
  <app-task-list></app-task-list>
</div>
Enter fullscreen mode Exit fullscreen mode

This was another mind-blowing moment for me. In jQuery, I would've had to write event handlers for each interaction, manually update the DOM, and keep track of state. With Angular, the framework handles all of that for us!

Angular vs jQuery: The Mental Shift

Let's compare how we'd approach the same task manager in jQuery versus Angular:

jQuery Approach:

  1. Select DOM elements
  2. Attach event handlers
  3. Manually update the DOM when data changes
  4. Handle state yourself

Angular Approach:

  1. Define component properties (your data)
  2. Create HTML templates with data binding
  3. Angular automatically updates the DOM
  4. State is managed within components

This shift from imperative to declarative programming was a game-changer for me. Instead of telling the browser exactly what to do, I'm just describing what I want the result to be, and Angular figures out how to make it happen.

Directives: Angular's Superpowers

Angular includes several built-in directives that extend HTML with new capabilities:

  • ngFor: Repeats a template for each item in a collection
  • ngIf: Conditionally includes or excludes a template
  • ngClass: Dynamically applies CSS classes
  • ngStyle: Dynamically applies styles
  • ngModel: Creates two-way data binding

These directives make dynamic UIs much easier to build. For example, instead of this jQuery code:

$('#task-list').empty();
tasks.forEach(function(task) {
  const $li = $('<li>').text(task.text);
  if (task.completed) {
    $li.addClass('completed');
  }
  $('#task-list').append($li);
});
Enter fullscreen mode Exit fullscreen mode

In Angular, we simply write:

<ul>
  <li *ngFor="let task of tasks" [class.completed]="task.completed">
    {{ task.text }}
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

The declarative nature of Angular's templates makes your code more readable and maintainable. When I first saw this in action, I thought, "This is how web development should have been all along!"

Services: Sharing Data Between Components

As your application grows, you'll want to share data between components. This is where services come in. Let's create a task service:

ng generate service task
Enter fullscreen mode Exit fullscreen mode
// task.service.ts
import { Injectable } from '@angular/core';

export interface Task {
  id: number;
  text: string;
  completed: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class TaskService {
  private tasks: Task[] = [];

  getTasks(): Task[] {
    return this.tasks;
  }

  addTask(text: string): void {
    this.tasks.push({
      id: Date.now(),
      text,
      completed: false
    });
  }

  deleteTask(id: number): void {
    this.tasks = this.tasks.filter(task => task.id !== id);
  }

  toggleComplete(id: number): void {
    this.tasks = this.tasks.map(task => {
      if (task.id === id) {
        return { ...task, completed: !task.completed };
      }
      return task;
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we can update our TaskListComponent to use this service:

// task-list.component.ts
import { Component } from '@angular/core';
import { TaskService, Task } from '../task.service';

@Component({
  selector: 'app-task-list',
  templateUrl: './task-list.component.html',
  styleUrls: ['./task-list.component.css']
})
export class TaskListComponent {
  newTaskText: string = '';

  constructor(private taskService: TaskService) {}

  get tasks(): Task[] {
    return this.taskService.getTasks();
  }

  addTask() {
    if (this.newTaskText.trim()) {
      this.taskService.addTask(this.newTaskText);
      this.newTaskText = '';
    }
  }

  toggleComplete(task: Task) {
    this.taskService.toggleComplete(task.id);
  }

  deleteTask(id: number) {
    this.taskService.deleteTask(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

This is a fundamental concept in Angular: dependency injection. When we declare a dependency in the constructor, Angular automatically provides an instance of that service. This makes testing and reusing

DevCycle image

OpenFeature Multi-Provider: Enabling New Feature Flagging Use-Cases

DevCycle is the first feature management platform with OpenFeature built in. We pair the reliability, scalability, and security of a managed service with freedom from vendor lock-in, helping developers ship faster with true OpenFeature-native feature flagging.

Watch Full Video πŸŽ₯

Top comments (0)

Tiger Data image

🐯 πŸš€ Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

πŸ‘‹ Kindness is contagious

Show gratitude for this enlightening post and join the vibrant DEV Community. Developers at every level are invited to share and grow our collective expertise.

A simple β€œthank you” can make someone’s day. Leave your appreciation below!

On DEV, collaborative knowledge clears our path and deepens our connections. Enjoyed the article? A quick message of thanks to the author goes a long way.

Count me in