1. How Were Components Declared Before Standalone Components?
What is component declaration?
Before standalone components, Angular applications were NgModule-based.
A component had to belong to an NgModule. Therefore, after creating a component, it was added to the module's:
declarations: []
For example:
@NgModule({
declarations: [
AppComponent,
HomeComponent,
ProductComponent
]
})
export class AppModule { }
The key idea was:
Components, directives, and pipes were declared inside an NgModule before they could participate in that module's templates.
Full Simple Sample — Before Standalone
Suppose we have:
AppComponent
↓
ProductComponent
The AppComponent wants to use:
<app-product></app-product>
product.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-product',
template: `
<h2>Product Component</h2>
<p>Laptop - ₹50,000</p>
`
})
export class ProductComponent {
}
Notice that there was no standalone configuration.
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>My Application</h1>
<app-product></app-product>
`
})
export class AppComponent {
}
AppComponent uses:
<app-product></app-product>
But AppComponent did not directly import ProductComponent.
Instead, both components were declared in an NgModule.
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ProductComponent } from './product.component';
@NgModule({
declarations: [
AppComponent,
ProductComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [
AppComponent
]
})
export class AppModule {
}
The important part is:
declarations: [
AppComponent,
ProductComponent
]
This tells Angular that these components belong to this NgModule.
How Did It Work?
AppModule
│
└── declarations
│
├── AppComponent
│
└── ProductComponent
Because both belonged to the same module, AppComponent could use ProductComponent:
<app-product></app-product>
What About Components From Another Module?
Suppose we had:
ProductModule
containing:
ProductComponent
Then we might write:
@NgModule({
declarations: [
ProductComponent
],
exports: [
ProductComponent
]
})
export class ProductModule {
}
Another module could then import:
imports: [
ProductModule
]
The relationship was:
ProductComponent
↓
declared in
↓
ProductModule
↓
exported from
↓
ProductModule
↓
imported by
↓
AppModule
↓
available to AppModule templates
This module-level indirection is one reason standalone components simplify dependency management.
Important Rule in Traditional Angular
A non-standalone component could normally be declared in only one NgModule.
For example, this was invalid:
@NgModule({
declarations: [
ProductComponent
]
})
export class AppModule { }
and also:
@NgModule({
declarations: [
ProductComponent
]
})
export class ProductModule { }
Angular would complain that ProductComponent was declared by multiple NgModules.
2. How Are Component Dependencies Handled With Standalone Components?
With standalone components, the component itself specifies the template dependencies it needs using:
imports: []
For example:
@Component({
imports: [
Product
]
})
export class App {
}
There is no need to put the component in an NgModule's:
declarations: []
Full Simple Standalone Example
Let's rewrite the previous application using modern standalone Angular.
product.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-product',
template: `
<h2>Product Component</h2>
<p>Laptop - ₹50,000</p>
`
})
export class Product {
}
In current Angular, components are standalone by default.
app.ts
import { Component } from '@angular/core';
import { Product } from './product';
@Component({
selector: 'app-root',
imports: [
Product
],
template: `
<h1>My Application</h1>
<app-product></app-product>
`
})
export class App {
}
The important part is:
imports: [
Product
]
Because the template uses:
<app-product></app-product>
the component imports Product directly.
Dependency Flow
Now the relationship is much simpler:
App
│
│ imports
↓
Product
Instead of:
AppComponent
↓
AppModule
↓
ProductModule
↓
ProductComponent
Standalone Components Can Import Other Components
For example:
@Component({
imports: [
Header,
ProductList,
Footer
]
})
export class App {
}
The template can then use:
<app-header></app-header>
<app-product-list></app-product-list>
<app-footer></app-footer>
The dependencies are visible directly from the component.
Standalone Components Can Import Directives
Suppose we use:
<a routerLink="/products">Products</a>
We can import RouterLink:
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-menu',
imports: [
RouterLink
],
template: `
<a routerLink="/">Home</a>
<a routerLink="/products">Products</a>
`
})
export class Menu {
}
So:
Template uses
routerLink
↓
Component imports
RouterLink
Standalone Components Can Import Pipes
Suppose the component uses:
{{ productName | uppercase }}
Then:
import { Component } from '@angular/core';
import { UpperCasePipe } from '@angular/common';
@Component({
selector: 'app-product',
imports: [
UpperCasePipe
],
template: `
<h2>{{ productName | uppercase }}</h2>
`
})
export class Product {
productName = 'laptop';
}
Dependency:
Template
↓
uppercase
↓
UpperCasePipe
↓
imports: [UpperCasePipe]
Standalone Components Can Import Other Standalone Dependencies
A realistic component might look like:
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import { CurrencyPipe } from '@angular/common';
import { ProductCard } from './product-card';
@Component({
selector: 'app-products',
imports: [
RouterLink,
CurrencyPipe,
ProductCard
],
template: `
<h2>Products</h2>
<p>{{ price | currency:'INR' }}</p>
<app-product-card></app-product-card>
<a routerLink="/">Back to Home</a>
`
})
export class Products {
price = 50000;
}
Here the component has three template dependencies:
Products
│
├── RouterLink
│
├── CurrencyPipe
│
└── ProductCard
All are visible directly in:
imports: [
RouterLink,
CurrencyPipe,
ProductCard
]
What About Services?
This is an important distinction.
Not every dependency goes into:
imports: []
imports is primarily for template dependencies, such as:
- Components
- Directives
- Pipes
- Compatible NgModules when needed
Services are obtained through dependency injection.
For example:
import { Component, inject } from '@angular/core';
import { ProductService } from './product.service';
@Component({
selector: 'app-products',
template: `
<h2>Products</h2>
`
})
export class Products {
private productService = inject(ProductService);
}
If the service has:
@Injectable({
providedIn: 'root'
})
export class ProductService {
}
you don't put it in:
imports: []
So remember:
Component / Directive / Pipe
↓
imports
Service
↓
Dependency Injection
Before vs Now
| Before standalone | Standalone Angular |
|---|---|
| Component declared in NgModule | No NgModule declaration required |
declarations: [] |
Not needed |
| NgModules controlled template scope | Component imports declares template dependencies |
| Often import another NgModule | Can directly import components/directives/pipes |
AppModule commonly required |
Root AppModule not required |
| Module-centric | Component-centric |
Easy Example to Remember
Before
If AppComponent needs ProductComponent:
AppComponent
│
↓
AppModule
↑
│
ProductComponent
Both were typically declared in:
declarations: [
AppComponent,
ProductComponent
]
Now
App
│
│ imports
↓
Product
Code:
@Component({
imports: [
Product
]
})
export class App {
}
This is the fundamental standalone model.
Key Points
Before standalone components
Components were declared inside an NgModule:
@NgModule({
declarations: [
AppComponent,
ProductComponent
]
})
export class AppModule {
}
The declarations array contained the module's components, directives and pipes.
With standalone components
The component directly imports the template dependencies it needs:
@Component({
imports: [
Product,
RouterLink,
CurrencyPipe
]
})
export class App {
}
No:
declarations: []
is required.
Very important distinction
Template dependency
(Component / Directive / Pipe)
↓
imports: []
Service dependency
↓
Dependency Injection
inject() / constructor
Interview Questions and Answers
1. How were components declared before standalone components?
Components were declared inside an Angular NgModule using the declarations array.
@NgModule({
declarations: [
AppComponent,
ProductComponent
]
})
export class AppModule { }
2. What was the purpose of declarations?
declarations registered the components, directives, and pipes that belonged to an NgModule.
declarations: [
ProductComponent
]
3. Can a traditional component be declared in multiple NgModules?
No. A non-standalone component should belong to only one NgModule's declarations.
If other modules needed it, its owning module could export it.
4. How are dependencies handled with standalone components?
Standalone components directly list their template dependencies in their own imports array.
@Component({
imports: [
Product,
RouterLink
]
})
export class App { }
5. Do standalone components use declarations?
No.
Standalone components don't need to be declared in an NgModule.
6. If one standalone component uses another component, what should we do?
Import the child component into the parent:
@Component({
imports: [
Product
]
})
export class App { }
Then:
<app-product></app-product>
7. Do services go into the component's imports array?
No. Services are normally provided through Angular's dependency-injection system.
For example:
private productService = inject(ProductService);
with:
@Injectable({
providedIn: 'root'
})
8. What can a standalone component's imports contain?
It can include template dependencies such as:
imports: [
ProductCard, // Component
RouterLink, // Directive
CurrencyPipe // Pipe
]
It can also interoperate with compatible NgModules when necessary.
9. What is the main architectural difference?
Before:
Component
↓
NgModule
↓
Other dependencies
Standalone:
Component
↓
Directly imports its template dependencies
Interview Quick Answer
Before standalone Angular, components, directives, and pipes were declared in an NgModule's
declarationsarray, and NgModules established their template scope. With standalone components, each component directly declares the components, directives, and pipes its template needs through itsimportsarray, while services continue to be handled through dependency injection.