Write, Run & Share Angular code online using OneCompiler's Angular online editor for free. It's one of the robust, feature-rich online editors for Angular. Getting started with the OneCompiler's Angular online editor is really simple and pretty fast. The editor shows sample boilerplate code when you choose language as 'Angular' and start writing code to learn and test online without worrying about tedious process of installation.
Angular is a comprehensive platform and framework for building single-page client applications using HTML and TypeScript. Developed by Google, Angular provides a complete solution with built-in features for routing, forms, HTTP client, and testing. It uses a component-based architecture with powerful features like dependency injection, reactive programming with RxJS, and ahead-of-time compilation.
Components are the fundamental building blocks of Angular applications, combining a TypeScript class with an HTML template and optional styles. The @Component decorator defines metadata like the selector (custom HTML tag), template, and styles. Angular 17+ supports standalone components that don't require NgModules. Components can receive data via inputs and emit events via outputs for parent-child communication.
// Basic component
import { Component } from '@angular/core';
@Component({
selector: 'app-hello',
standalone: true,
template: `
<h1>Hello, {{ name }}!</h1>
<button (click)="greet()">Greet</button>
`,
styles: [`
h1 { color: #5C6AC4; }
`]
})
export class HelloComponent {
name = 'World';
greet() {
console.log('Hello, ' + this.name);
}
}
// Component with inputs and outputs
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<button (click)="onSelect()">Select</button>
</div>
`
})
export class UserCardComponent {
@Input() user!: { name: string; email: string };
@Input({ required: true }) id!: number; // Required input
@Output() selected = new EventEmitter<number>();
onSelect() {
this.selected.emit(this.id);
}
}
// Usage in parent template
// <app-user-card [user]="currentUser" [id]="1" (selected)="handleSelect($event)" />
Angular templates use a special syntax for binding data, handling events, and controlling the DOM structure. Property binding uses square brackets [property], event binding uses parentheses (event), and two-way binding combines both [(ngModel)]. Template reference variables (#ref) provide direct access to DOM elements or component instances from within the template.
<!-- Text interpolation -->
<p>{{ message }}</p>
<p>{{ user.name }}</p>
<p>{{ getFullName() }}</p>
<p>{{ price | currency }}</p>
<!-- With pipe -->
<!-- Property binding -->
<img [src]="imageUrl" />
<button [disabled]="isDisabled">Click</button>
<div [class.active]="isActive">Active class</div>
<div [style.color]="textColor">Styled text</div>
<div [ngClass]="{'active': isActive, 'disabled': isDisabled}"></div>
<div [ngStyle]="{'font-size': fontSize + 'px'}"></div>
<!-- Event binding -->
<button (click)="handleClick()">Click</button>
<button (click)="handleClick($event)">With event</button>
<input (input)="onInput($event)" />
<input (keyup.enter)="onEnter()" />
<form (ngSubmit)="onSubmit()">
<!-- Two-way binding (requires FormsModule) -->
<input [(ngModel)]="username" />
<!-- Equivalent to: -->
<input [ngModel]="username" (ngModelChange)="username = $event" />
<!-- Template reference variable -->
<input #nameInput type="text" />
<button (click)="greet(nameInput.value)">Greet</button>
<!-- Safe navigation operator (optional chaining) -->
<p>{{ user?.address?.city }}</p>
<!-- Non-null assertion -->
<p>{{ user!.name }}</p>
</form>
Angular 17+ introduced a new built-in control flow syntax using @if, @for, and @switch blocks directly in templates. These replace the older structural directives (*ngIf, *ngFor, *ngSwitch) with a more readable and performant syntax. The new control flow is automatically available without imports and provides better type narrowing in template expressions.
<!-- Conditional rendering (@if) -->
@if (isLoggedIn) {
<p>Welcome, {{ user.name }}!</p>
} @else if (isLoading) {
<p>Loading...</p>
} @else {
<p>Please log in</p>
}
<!-- Loops (@for) -->
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items found</li>
}
<!-- With index and other variables -->
@for (item of items; track item.id; let i = $index, first = $first, last =
$last) {
<li [class.first]="first" [class.last]="last">{{ i + 1 }}. {{ item.name }}</li>
}
<!-- Switch (@switch) -->
@switch (status) { @case ('pending') {
<span class="badge pending">Pending</span>
} @case ('approved') {
<span class="badge approved">Approved</span>
} @default {
<span class="badge">Unknown</span>
} }
<!-- Legacy structural directives (still supported) -->
<div *ngIf="condition">Shown if true</div>
<div *ngIf="condition; else elseBlock">If true</div>
<ng-template #elseBlock>If false</ng-template>
<li *ngFor="let item of items; index as i; trackBy: trackByFn">
{{ i }}: {{ item.name }}
</li>
Services are classes that encapsulate reusable business logic, data access, or shared functionality across components. Angular's dependency injection (DI) system manages service instances and injects them where needed. The @Injectable decorator marks a class as injectable, and providedIn: 'root' makes it a singleton available throughout the app. Components request services through constructor parameters.
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root' // Singleton, available app-wide
})
export class UserService {
private apiUrl = '/api/users';
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
getUser(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}
createUser(user: User): Observable<User> {
return this.http.post<User>(this.apiUrl, user);
}
updateUser(id: number, user: User): Observable<User> {
return this.http.put<User>(`${this.apiUrl}/${id}`, user);
}
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}
// Using service in component
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
standalone: true,
template: `
@for (user of users; track user.id) {
<div>{{ user.name }}</div>
}
`
})
export class UserListComponent {
private userService = inject(UserService); // inject() function
// Or: constructor(private userService: UserService) {}
users: User[] = [];
ngOnInit() {
this.userService.getUsers().subscribe(users => {
this.users = users;
});
}
}
Angular provides two approaches to forms: template-driven forms for simple cases and reactive forms for complex scenarios with dynamic fields and validation. Template-driven forms use directives like ngModel and are defined in the template. Reactive forms use FormGroup and FormControl classes, defined in TypeScript for better control and testability.
// Template-driven forms (simple)
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-login',
standalone: true,
imports: [FormsModule],
template: `
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm)">
<input name="email" [(ngModel)]="email" required email #emailInput="ngModel">
@if (emailInput.invalid && emailInput.touched) {
<span class="error">Valid email required</span>
}
<input name="password" type="password" [(ngModel)]="password" required minlength="6">
<button type="submit" [disabled]="loginForm.invalid">Login</button>
</form>
`
})
export class LoginComponent {
email = '';
password = '';
onSubmit(form: any) {
console.log('Form data:', form.value);
}
}
// Reactive forms (complex)
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-register',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<input formControlName="name" placeholder="Name">
<input formControlName="email" placeholder="Email">
<input formControlName="password" type="password" placeholder="Password">
<div formGroupName="address">
<input formControlName="street" placeholder="Street">
<input formControlName="city" placeholder="City">
</div>
@if (registerForm.get('email')?.invalid && registerForm.get('email')?.touched) {
<span class="error">Valid email required</span>
}
<button type="submit" [disabled]="registerForm.invalid">Register</button>
</form>
`
})
export class RegisterComponent {
registerForm: FormGroup;
constructor(private fb: FormBuilder) {
this.registerForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
address: this.fb.group({
street: [''],
city: ['', Validators.required]
})
});
}
onSubmit() {
if (this.registerForm.valid) {
console.log('Form data:', this.registerForm.value);
}
}
}
Angular Router enables navigation between views in a single-page application, mapping URL paths to components. Routes are defined as an array of path-component pairs, supporting dynamic parameters, child routes, and lazy loading. The <router-outlet> directive marks where routed components should render. Navigation guards protect routes and control access.
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'users/:id', component: UserDetailComponent }, // Dynamic parameter
{
path: 'admin',
loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent), // Lazy load
canActivate: [authGuard] // Guard
},
{
path: 'dashboard',
component: DashboardComponent,
children: [
{ path: 'profile', component: ProfileComponent },
{ path: 'settings', component: SettingsComponent }
]
},
{ path: '**', component: NotFoundComponent } // Wildcard
];
// app.config.ts
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig = {
providers: [provideRouter(routes)]
};
<!-- Navigation -->
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
<a [routerLink]="['/users', userId]">User</a>
<a [routerLink]="['/search']" [queryParams]="{q: 'angular'}">Search</a>
</nav>
<router-outlet></router-outlet>
// Accessing route params in component
import { Component, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
@Component({ ... })
export class UserDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
ngOnInit() {
// Get route parameters
this.route.params.subscribe(params => {
console.log('User ID:', params['id']);
});
// Get query parameters
this.route.queryParams.subscribe(params => {
console.log('Search:', params['q']);
});
}
goToUsers() {
this.router.navigate(['/users']);
// Or with query params:
this.router.navigate(['/search'], { queryParams: { q: 'angular' } });
}
}
Signals are Angular's new reactive primitive (Angular 16+) that provide fine-grained reactivity without RxJS complexity. A signal holds a value and notifies consumers when it changes. Computed signals derive values from other signals and update automatically. Effects run side effects when signal values change. Signals integrate seamlessly with templates and offer better performance than zone-based change detection.
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>
<button (click)="increment()">+1</button>
<button (click)="decrement()">-1</button>
<button (click)="reset()">Reset</button>
`
})
export class CounterComponent {
// Writable signal
count = signal(0);
// Computed signal (read-only, auto-updates)
doubleCount = computed(() => this.count() * 2);
constructor() {
// Effect: runs when dependencies change
effect(() => {
console.log('Count changed to:', this.count());
});
}
increment() {
this.count.update(c => c + 1);
// Or: this.count.set(this.count() + 1);
}
decrement() {
this.count.update(c => c - 1);
}
reset() {
this.count.set(0);
}
}
// Signal-based inputs (Angular 17.1+)
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-user',
standalone: true,
template: `<p>{{ name() }}</p>`
})
export class UserComponent {
name = input<string>(); // Optional input signal
id = input.required<number>(); // Required input signal
selected = output<number>(); // Output signal
select() {
this.selected.emit(this.id());
}
}
Angular's HttpClient provides a powerful way to make HTTP requests with built-in support for JSON parsing, error handling, and request/response interceptors. The client returns RxJS Observables, enabling reactive data handling with operators for transformation, retry logic, and error recovery. Interceptors can add authentication headers, log requests, or handle errors globally.
// HTTP requests
import { Component, inject } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { catchError, map, retry } from 'rxjs/operators';
@Component({ ... })
export class DataComponent {
private http = inject(HttpClient);
// GET request
getUsers() {
return this.http.get<User[]>('/api/users');
}
// GET with parameters
searchUsers(query: string) {
const params = new HttpParams()
.set('q', query)
.set('limit', '10');
return this.http.get<User[]>('/api/users', { params });
}
// POST request
createUser(user: User) {
return this.http.post<User>('/api/users', user);
}
// PUT request
updateUser(id: number, user: User) {
return this.http.put<User>(`/api/users/${id}`, user);
}
// DELETE request
deleteUser(id: number) {
return this.http.delete<void>(`/api/users/${id}`);
}
// With headers and error handling
getProtectedData() {
const headers = new HttpHeaders()
.set('Authorization', 'Bearer token');
return this.http.get('/api/protected', { headers }).pipe(
retry(3),
catchError(error => {
console.error('Error:', error);
throw error;
})
);
}
}
// HTTP Interceptor
import { HttpInterceptorFn } from '@angular/common/http';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = localStorage.getItem('token');
if (token) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
}
return next(req);
};
// Register in app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
export const appConfig = {
providers: [
provideHttpClient(withInterceptors([authInterceptor]))
]
};
Pipes transform data for display in templates without modifying the underlying values. Angular provides built-in pipes for common transformations like dates, currency, percentages, and JSON formatting. Custom pipes can be created for application-specific transformations. Pipes can be pure (stateless, cached) or impure (recalculated on every change detection cycle).
<!-- Built-in pipes -->
<p>{{ birthday | date }}</p>
<p>{{ birthday | date:'fullDate' }}</p>
<p>{{ birthday | date:'yyyy-MM-dd' }}</p>
<p>{{ price | currency }}</p>
<p>{{ price | currency:'EUR':'symbol':'1.2-2' }}</p>
<p>{{ ratio | percent }}</p>
<p>{{ ratio | percent:'1.1-2' }}</p>
<p>{{ name | uppercase }}</p>
<p>{{ name | lowercase }}</p>
<p>{{ name | titlecase }}</p>
<p>{{ longText | slice:0:100 }}...</p>
<p>{{ data | json }}</p>
<!-- Async pipe (auto-subscribes to Observable/Promise) -->
<p>{{ user$ | async }}</p>
@if (users$ | async; as users) { @for (user of users; track user.id) {
<p>{{ user.name }}</p>
} }
<!-- Chaining pipes -->
<p>{{ birthday | date:'fullDate' | uppercase }}</p>
// Custom pipe
import { Pipe, PipeTransform } from "@angular/core"
@Pipe({
name: "truncate",
standalone: true,
})
export class TruncatePipe implements PipeTransform {
transform(
value: string,
limit: number = 50,
ellipsis: string = "..."
): string {
if (!value || value.length <= limit) return value
return value.substring(0, limit) + ellipsis
}
}
// Usage: {{ longText | truncate:100:'...' }}
// Filter pipe example
@Pipe({ name: "filter", standalone: true })
export class FilterPipe implements PipeTransform {
transform(items: any[], field: string, value: string): any[] {
if (!items || !value) return items
return items.filter((item) =>
item[field].toLowerCase().includes(value.toLowerCase())
)
}
}
// Usage: @for (item of items | filter:'name':searchTerm; track item.id)