SoFunction
Updated on 2025-04-12

Detailed explanation of the usage of Angular NgIf

NgIf command function

The ngIf directive is used to render the contents of the then or else template at a specified location based on the value of the expression.

  1. Then templates are the inline templates associated with the ngIf directive by default unless bound to a different value.
  2. The else template defaults to null unless the corresponding value is bound.

NgIf instruction syntax

Simple form

<!--Syntactic sugar-->
<div *ngIf="condition">...</div>
<!--Angular Used intemplate-->
<ng-template [ngIf]="condition"><div>...</div></ng-template>

Use else block

<div *ngIf="condition; else elseBlock">...</div>
<ng-template #elseBlock>...</ng-template>

Use then and else blocks

<div *ngIf="condition; then thenBlock else elseBlock"></div>
<ng-template #thenBlock>...</ng-template>
<ng-template #elseBlock>...</ng-template>

Use as syntax

<div *ngIf="condition as value; else elseBlock">{{value}}</div>
<ng-template #elseBlock>...</ng-template>

NgIf usage example

@Component({
 selector: 'ng-if-then-else',
 template: `
  <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
  <button (click)="switchPrimary()">Switch Primary</button>
    show = {{show}}
  <br>
  <div *ngIf="show; then thenBlock; else elseBlock">this is ignored</div>
  <ng-template #primaryBlock>Primary text to show</ng-template>
  <ng-template #secondaryBlock>Secondary text to show</ng-template>
  <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
 `
})
class NgIfThenElse implements OnInit {
 thenBlock: TemplateRef<any> = null;
 show: boolean = true;
 
 @ViewChild('primaryBlock')
 primaryBlock: TemplateRef<any> = null;
 @ViewChild('secondaryBlock')
 secondaryBlock: TemplateRef<any> = null;
 
 switchPrimary() {
   =  ===  ? 
    : ;
 }
 
 ngOnInit() { 
    = ;
 }
}

Basic knowledge

TemplateRef
The TemplateRef instance is used to represent template objects. The definition of the TemplateRef abstract class is as follows:

// angular\packages\core\src\linker\template_ref.ts
export abstract class TemplateRef<C> {
 abstract get elementRef(): ElementRef;
 abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}

ViewContainerRef

The ViewContainerRef instance provides createEmbeddedView() method, this method receivesTemplateRef The object is used as a parameter and the contents in the template are inserted into the page as sibling elements of the container (comment element).

NgIfContext

The NgIfContext instance is used to represent the NgIf context.

// angular\packages\common\src\directives\ng_if.ts
export class NgIfContext {
 public $implicit: any = null;
 public ngIf: any = null;
}

NgIf source code analysis

NgIf directive definition

@Directive({
  selector: '[ngIf]' // Attribute selector - <ng-template [ngIf]="condition">})

NgIf class private properties and constructors

export class NgIf {
 // Create NgIfContext context private _context: NgIfContext = new NgIfContext();
 // represents then template object private _thenTemplateRef: TemplateRef&lt;NgIfContext&gt;|null = null;
 // represents an else template object private _elseTemplateRef: TemplateRef&lt;NgIfContext&gt;|null = null;

 // Represents the EmbeddedViewRef view created based on the then template private _thenViewRef: EmbeddedViewRef&lt;NgIfContext&gt;|null = null;
 // Represents the EmbeddedViewRef view created based on the else template private _elseViewRef: EmbeddedViewRef&lt;NgIfContext&gt;|null = null;

 constructor(
  private _viewContainer: ViewContainerRef, 
  templateRef: TemplateRef&lt;NgIfContext&gt;) {
   this._thenTemplateRef = templateRef; //The default value of the then template is the inline template associated with the ngIf directive }
}

NgIf class input properties

@Input()
set ngIf(condition: any) {
  this._context.$implicit = this._context.ngIf = condition;
  this._updateView(); // Update the view}

@Input()
set ngIfThen(templateRef: TemplateRef&lt;NgIfContext&gt;) {
  this._thenTemplateRef = templateRef;
  this._thenViewRef = null; // Clear the previously created view  this._updateView();
}

@Input()
set ngIfElse(templateRef: TemplateRef&lt;NgIfContext&gt;) {
  this._elseTemplateRef = templateRef;
  this._elseViewRef = null; // Clear the previously created view  this._updateView();
}

_updateView() private method

// Update the viewprivate _updateView() {
 // this._context.$implicit = this._context.ngIf = condition
 // If the value of the condition expression is truthy if (this._context.$implicit) {
 // If _thenViewRef is null and _thenTemplateRef exists, create an embedded view of _thenViewRef   if (!this._thenViewRef) {
    this._viewContainer.clear();
    this._elseViewRef = null;
    if (this._thenTemplateRef) {
     this._thenViewRef =
       this._viewContainer.createEmbeddedView(this._thenTemplateRef,
        this._context);
    }
   }
  } else { // The value of the condition expression is false   // If _elseViewRef is null and _elseTemplateRef exists, create an embedded view of _elseViewRef   if (!this._elseViewRef) {
    this._viewContainer.clear();
    this._thenViewRef = null;
    if (this._elseTemplateRef) {
     this._elseViewRef =
       this._viewContainer.createEmbeddedView(this._elseTemplateRef, 
        this._context);
    }
   }
  }
}

ngIf The source code of the instruction is relatively simple, the most important thing is_updateView() method. The most important function in this method is how to create embedded views based on template objects. Next, let's analyze itViewContainerRef The object's createEmbeddedView() method.

ViewContainerRef - createEmbeddedView()

Method signature

// angular\packages\core\src\linker\view_container_ref.ts
export abstract class ViewContainerRef {
  /**
   * Create an Embedded View based on the TemplateRef object, and then insert it into the container according to the value specified by `index`. 
   * If no value of `index` is specified, the newly created view will be inserted as the last view in the container.
   */ 
 abstract createEmbeddedView&lt;C&gt;(
   templateRef: TemplateRef&lt;C&gt;, 
   context?: C, index?: number):
   EmbeddedViewRef&lt;C&gt;;
}

Method implementation

// angular\packages\core\src\view\
class ViewContainerRef_ implements ViewContainerData {
  // ...
  createEmbeddedView&lt;C&gt;(
   templateRef: TemplateRef&lt;C&gt;, 
   context?: C, index?: number):
   EmbeddedViewRef&lt;C&gt; {
    //Create the TemplateRef object createEmbeddedView() method to createEmbeddedViewRef object    const viewRef = (context || &lt;any&gt;{});
     // Insert into the view container according to the specified index value    (viewRef, index);
    return viewRef;
 }
}

// ViewContainerData interface inherits from ViewContainerRef abstract classexport interface ViewContainerData extends ViewContainerRef {
 _embeddedViews: ViewData[];
}

export interface ViewData {
 def: ViewDefinition;
 root: RootData;
 renderer: Renderer2;
 parentNodeDef: NodeDef|null;
 parent: ViewData|null;
 viewContainerParent: ViewData|null;
 component: any;
 context: any;
 nodes: {[key: number]: NodeData};
 state: ViewState;
 oldValues: any[];
 disposables: DisposableFn[]|null;
}

By observingViewContainerRef_ In the class createEmbeddedView() Method, we found that the method is called internallyTemplateRef The object'screateEmbeddedView() Method to create an embedded view. So let's analyze it nextTemplateRefThe object'screateEmbeddedView() method.

TemplateRef - createEmbeddedView()

Method signature

// angular\packages\core\src\linker\template_ref.ts
export abstract class TemplateRef<C> {
 abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}

Method implementation

// angular\packages\core\src\view\
class TemplateRef_ extends TemplateRef<any> implements TemplateData {
 // ...
 createEmbeddedView(context: any): EmbeddedViewRef<any> {
  return new ViewRef_((
    this._parentView, this._def, this._def.element !.template !, context));
 }
}

export interface TemplateData extends TemplateRef<any> {
 _projectedViews: ViewData[];
}

After reading the above source code, there is no doubt that we will continue to analyze nextServices In the object createEmbeddedView() method.

Services - createEmbeddedView()

Services object definition

// angular\packages\core\src\view\
export const Services: Services = {
 setCurrentNode: undefined !,
 createRootView: undefined !,
 createEmbeddedView: undefined !,
 createComponentView: undefined !,
 createNgModuleRef: undefined !,
 overrideProvider: undefined !,
 clearProviderOverrides: undefined !,
 checkAndUpdateView: undefined !,
 checkNoChangesView: undefined !,
 destroyView: undefined !,
 resolveDep: undefined !,
 createDebugContext: undefined !,
 handleEvent: undefined !,
 updateDirectives: undefined !,
 updateRenderer: undefined !,
 dirtyParentQueries: undefined !,
};

Services object initialization

// angular\packages\core\src\view\
export function initServicesIfNeeded() {
 if (initialized) {
  return;
 }
 initialized = true;
 const services = isDevMode() ? createDebugServices() : createProdServices();
  = ;
  = ;
  = ;
  = ;
  = ;
  = ;
  = ;
  = ;
  = ;
  = ;
  = resolveDep;
  = ;
  = ;
  = ;
  = ;
  = dirtyParentQueries;
}

existinitServicesIfNeeded()In the method, different Services objects are created according to the current schema. Next, let's take a lookcreateProdServices() method:

function createProdServices() {
 return {
  setCurrentNode: () =&gt; {},
  createRootView: createProdRootView,
  createEmbeddedView: createEmbeddedView // Other methods have been omitted}

createEmbeddedView() method

// angular\packages\core\src\view\
export function createEmbeddedView(
  parent: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData {
 // embedded views are seen as siblings to the anchor, so we need
 // to get the parent of the anchor and use it as parentIndex.
 // Create ViewData object const view = createView(, , parent, anchorDef, viewDef);
 // Initialize ViewData object - set the value of component and context attributes initView(view, , context);
 // Create nodes in the view, that is, set the attribute value of the array // const nodes = ; for(...) { ...; nodes[i] = nodeData; }
 createViewNodes(view);
 return view;
}

At this time, it is found that if all methods are fully analyzed, there will be too much content. The source code analysis ends here. Interested readers please read the source code yourself (please forgive me for your readers). Next, let's summarize createEmbeddedView() Method calling process:

ViewContainerRef_ -> createEmbeddedView()
  => TemplateRef_ -> createEmbeddedView()
  => Services -> createEmbeddedView()
   => Call createEmbeddedView()

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.