# Automatic Component Dependency Registration
## Overview
The framework now automatically registers all component dependencies before rendering. When you bootstrap or create a component, all components listed in its `imports` array are recursively registered as native Web Components.
## How It Works
### Registration Flow
```typescript
@Component({
selector: 'app-root',
template: '',
imports: [DashboardComponent],
})
export class AppComponent {}
// When bootstrapping:
Core.bootstrap(AppComponent);
```
**Internal Process:**
1. `Core.bootstrap(AppComponent)` is called
2. `WebComponentFactory.createFromElement()` is invoked
3. `registerWithDependencies(appComponentInstance)` is called
4. Framework checks `imports: [DashboardComponent]`
5. Creates `DashboardComponent` instance via Injector
6. Recursively calls `registerWithDependencies(dashboardComponentInstance)`
7. Registers `DashboardComponent` as `` custom element
8. Registers `AppComponent` as `` custom element
9. Renders the template with all dependencies ready
### Code Implementation
```typescript
// In WebComponentFactory
static registerWithDependencies(component: IComponent): boolean {
const imports = component._quarcComponent[0].imports || [];
const injector = Injector.get();
// Register all imported components first
for (const importItem of imports) {
if (this.isComponentType(importItem)) {
let componentInstance = injector.createInstance(
importItem as Type
);
if (!componentInstance._quarcComponent) {
componentInstance = importItem as unknown as IComponent;
}
if (componentInstance._quarcComponent) {
// Recursive call for nested dependencies
this.registerWithDependencies(componentInstance);
}
}
}
// Finally register the parent component
return this.tryRegister(component);
}
```
## Example: Multi-Level Dependencies
```typescript
// Level 3: Icon Component (no dependencies)
@Component({
selector: 'app-icon',
template: '',
})
export class IconComponent {}
// Level 2: Button Component (depends on Icon)
@Component({
selector: 'app-button',
template: `
`,
imports: [IconComponent],
})
export class ButtonComponent {}
// Level 1: Dashboard Component (depends on Button)
@Component({
selector: 'dashboard',
template: `
Dashboard
`,
imports: [ButtonComponent],
})
export class DashboardComponent {}
// Level 0: Root Component (depends on Dashboard)
@Component({
selector: 'app-root',
template: '',
imports: [DashboardComponent],
})
export class AppComponent {}
```
**Registration Order:**
1. `IconComponent` → ``
2. `ButtonComponent` → ``
3. `DashboardComponent` → ``
4. `AppComponent` → ``
## Benefits
### ✅ No Manual Registration
Before:
```typescript
// Manual registration required
WebComponentFactory.tryRegister(iconComponent);
WebComponentFactory.tryRegister(buttonComponent);
WebComponentFactory.tryRegister(dashboardComponent);
Core.bootstrap(AppComponent);
```
After:
```typescript
// Automatic registration
Core.bootstrap(AppComponent); // All dependencies registered automatically
```
### ✅ Correct Order Guaranteed
The framework ensures components are registered in the correct dependency order, preventing errors where a parent tries to use an unregistered child component.
### ✅ Prevents Duplicate Registration
The `tryRegister()` method checks if a component is already registered and returns `false` if it is, preventing duplicate registration errors.
### ✅ Supports Circular Dependencies
If two components import each other (not recommended but possible), the registration system handles it gracefully by checking if a component is already registered before attempting to register it again.
### ✅ Works with Lazy Loading
Components are only registered when they're actually needed, supporting lazy loading patterns:
```typescript
// Only registers when loaded
const lazyComponent = await import('./lazy.component');
WebComponentFactory.create(lazyComponent.LazyComponent);
```
## Type Checking
The `isComponentType()` helper ensures only valid components are processed:
```typescript
private static isComponentType(item: any): boolean {
// Check if it's a class constructor
if (typeof item === 'function') {
return true;
}
// Check if it's already an instance with metadata
if (item && typeof item === 'object' && item._quarcComponent) {
return true;
}
return false;
}
```
## Integration with Injector
The framework uses the `Injector` to create component instances, which:
- Resolves constructor dependencies
- Caches instances for reuse
- Supports dependency injection patterns
```typescript
const injector = Injector.get();
let componentInstance = injector.createInstance(
importItem as Type
);
```
## Error Handling
Registration errors are caught and logged without breaking the application:
```typescript
try {
customElements.define(tagName, WebComponentClass);
this.registeredComponents.set(tagName, WebComponentClass);
this.componentInstances.set(tagName, component);
return true;
} catch (error) {
console.warn(`Failed to register component ${tagName}:`, error);
return false;
}
```
Common errors:
- **Duplicate registration**: Component already registered (handled gracefully)
- **Invalid tag name**: Tag name doesn't contain a hyphen (auto-converted)
- **Constructor errors**: Component constructor throws an error (logged)
## Best Practices
### 1. Always Declare Imports
```typescript
@Component({
selector: 'parent',
template: '',
imports: [ChildComponent], // ✅ Declared
})
export class ParentComponent {}
```
### 2. Avoid Circular Dependencies
```typescript
// ❌ Avoid this
@Component({
selector: 'comp-a',
imports: [ComponentB],
})
export class ComponentA {}
@Component({
selector: 'comp-b',
imports: [ComponentA], // Circular!
})
export class ComponentB {}
```
### 3. Use Lazy Loading for Large Dependencies
```typescript
@Component({
selector: 'app-root',
template: '',
// Don't import heavy components here
})
export class AppComponent {
async loadHeavyComponent() {
const { HeavyComponent } = await import('./heavy.component');
WebComponentFactory.create(HeavyComponent);
}
}
```
## Debugging
Enable console logging to see the registration flow:
```typescript
// In WebComponentFactory.registerWithDependencies
console.log(`Registering dependencies for: ${component._quarcComponent[0].selector}`);
// In WebComponentFactory.tryRegister
console.log(`Registering component: ${tagName}`);
```
## Migration from Manual Registration
If you have existing code with manual registration:
**Before:**
```typescript
WebComponentFactory.tryRegister(childComponent);
WebComponentFactory.tryRegister(parentComponent);
```
**After:**
```typescript
// Just use the parent, children are registered automatically
WebComponentFactory.create(parentComponent);
```
Or simply:
```typescript
Core.bootstrap(ParentComponent);
```
## Summary
The automatic dependency registration system:
- ✅ Registers components in correct order
- ✅ Handles nested dependencies recursively
- ✅ Prevents duplicate registrations
- ✅ Integrates with dependency injection
- ✅ Provides error handling and logging
- ✅ Supports lazy loading patterns
- ✅ Simplifies component usage
No manual registration needed - just declare your imports and bootstrap your app!