35

What is the alternative of ng-init="myText='Hello World!'" in Angular 2 to add in the template, not in the component

 <div ng-app="" ng-init="myText='Hello World!'">

the alternative in Angular 2

PitaJ
  • 12,969
  • 6
  • 36
  • 55
Shamil
  • 727
  • 1
  • 9
  • 17

10 Answers10

29

You can use a directive

@Directive({
  selector: 'ngInit',
  exportAs: 'ngInit'
}) 
export class NgInit {
  @Input() values: any = {};

  @Input() ngInit;
  ngOnInit() {
    if(this.ngInit) { this.ngInit(); }
  }  
}

you can use it to pass a function to be called like

<div [ngInit]="doSomething"

or to make values available

<div ngInit [values]="{a: 'a', b: 'b'}" #ngInit="ngInit">
  <button (click)="clickHandler(ngInit.values.a)">click me</button>
</div>
  • ngInit addes the directive
  • [values]="{a: 'a', b: 'b'}" sets some initial values
  • #ngInit="ngInit" creates a reference for later use
  • ngInit.values.a reads the a value from the created reference.

See also Converting Angular 1 to Angular 2 ngInit function

Mohammad Kermani
  • 5,188
  • 7
  • 37
  • 61
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    here is a plunker: https://plnkr.co/edit/HmWHjM8wa8gIhuClGdO2?p=preview . in almost all cases if you are looking at this you should probably ```ng g component youNeedAComponent``` +1 for exportAs example. – Leblanc Meneses Jul 28 '17 at 06:48
  • 1
    nice hack. :) (y) – Ashish Ratan Aug 17 '17 at 07:39
  • How about passing a parm in method like `
    – Sharan Ainapurapu Sep 13 '17 at 03:09
  • Binding to functions is usually a bad idea, because it will be called every time change detection runs. Besides that I don't understand your question. There shouldn't be any need to handle it in a special way. – Günter Zöchbauer Sep 13 '17 at 03:34
  • @Rishi don't know what you mean. Why would it not work with `*ngFor`? – Günter Zöchbauer Jun 07 '18 at 11:33
  • @GünterZöchbauer actually I tried passing it to another component and I changed the value from there, which is not working. In my case I have emitted an event to update in the parent scope. [link](https://angular-5-tutorial-5unhre.stackblitz.io) – Rishi0405 Jun 07 '18 at 13:03
  • @Rishi the link only shows the running app, not the source. please use the share button to create a share link. – Günter Zöchbauer Jun 07 '18 at 13:11
  • Look down for the answer with the Output() and EventEmitter. This is the correct way to do it because passing FN as a reference will make you lose the `this` reference unless using fat arrow syntax. Also, using the Output() syntaxt let you pass parameters on the FN call. – David Lacroix Feb 11 '20 at 14:17
10

Another approach is by using the @Output decorator and EventEmitter:

import {Directive, OnInit, Output, EventEmitter} from '@angular/core';

@Directive({
    selector: '[ngInit]'
})
export class NgInitDirective implements OnInit {

    @Output()
    ngInit: EventEmitter<any> = new EventEmitter();

    ngOnInit() {
        this.ngInit.emit();
    }
}

And then use it like:

<div *ngIf="condition" (ngInit)="initialize()">  ... </div>

Demo

Augusto Barreto
  • 3,637
  • 4
  • 29
  • 39
5
@Directive({
  selector: '[ngxInit]',
})
export class NgxInitDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) {
  }

  @Input() set ngxInit(val: any) {
    this.viewContainer.clear();
    this.viewContainer.createEmbeddedView(this.templateRef, {ngxInit: val});
  }
}

a value expression is set via *ngxInit and published using as micro-syntax:

<div *ngxInit="3 * i + j as idx">{{idx}}</div>

published as https://www.npmjs.com/package/ngx-init

Amit Portnoy
  • 5,957
  • 2
  • 29
  • 30
4

You do not always need a custom directive for this. If you're okay with your function being called more than once, you can simple do:

<input #input [attr.init]="resizeInput(input)"/>

The word "init" there is completely arbitrary. The downside is yourInitFunction will get called on every digest cycle.

Note, if you return anything from your function this will add an attribute called "init" to your element with the returned value. If you return undefined, it will not add the attribute.

This is normally a non-issue, just keep it in mind.

parliament
  • 21,544
  • 38
  • 148
  • 238
3

It is possible by using OnInit Life Cycle hook as below,

  1. Import OnInit from core library

    import {Component, OnInit} from '@angular/core'
    
  2. Implement it to your component class

    export class App  implements OnInit {
    
    }
    
  3. Implement the ngOnInit method

     ngOnInit(){
        this.myText='Hello World!'
    
       }
    

LIVE DEMO

Aravind
  • 40,391
  • 16
  • 91
  • 110
2

While I agree that initialization should go into the ngOnInit life-cycle hook, it should also be noted that you can use the constructor of the component to initialize class members. In your simple example, you could even use the member declaration to set the variable, e.g.:

@Component({ template: '<div>{{myText}}</div>' })
export class MyComponent {
    myText = 'Hello World!';
}
Philipp Aumayr
  • 1,400
  • 11
  • 14
0

Little Update! In the latest versions of Angular this will not work:

@Directive({
 selector: 'ngInit',
 exportAs: 'ngInit'
})

you should use '[]':

@Directive({
   selector: '[ngInit]',
   exportAs: 'ngInit'
})
Anton Pegov
  • 1,413
  • 2
  • 20
  • 33
0

A possible improvement over Günter's answer:

@Directive({
  selector: 'ngInit',
  exportAs: 'ngInit'
}) 
export class NgInit {
  @Input() ngInit: () => any;
  ngOnInit() {
    if(typeof this.ngInit === 'function') { 
        this.ngInit(); 
    } else {
        // preventing re-evaluation (described below)
        throw 'something';
    }
  }  
}

And then use higher-order functions for passing in data, like so:

// component.ts
myInitFunction(info) {
  // returns another function
  return () => console.log(info);
}

If you use a higher-order function like this, you also don't need to worry about what this is inside of myInitFunction since an arrow function is really what is passed.

Use the directive like so:

// component.html
<another-component #ref></another-component>
<div [ngInit]="myInitFunction(ref)"></div>

If you were to try and create a directive that doesn't pass in a function as the input in the manner described here, you run the risk of infinite loops. For example, you'd get that if you whole directive was simply evaluating the expression you gave it.

This is what would happen if your myInitFunction method didn't return another function (and your HTML was the same as above). You'd console out, return undefined, and then change detection would re-evaluate it, consoling out over and over.

Kevin Beal
  • 10,500
  • 12
  • 66
  • 92
0

I found a fairly easy fix for this problem using the data attribute. In my example, i just use it to show, but a toggle or whatever is possible using the same method obviously.

<div data-expand="false" #expandWrap>
  <span *ngIf="expandWrap.dataset.expand == 'false'">
    {{'Test expand without..'}}
    <span (click)="expandWrap.dataset.expand = 'true'">more</span>
  </span>
  <span *ngIf="expandWrap.dataset.expand == 'true'">
       {{'Test expand with more text because now we are expanded!'}}
  </span>
</div>

I made a small demo on stackblitz on how this works

Nebulosar
  • 1,727
  • 3
  • 20
  • 46
0

How about this?:

@Directive({
  selector: '[onInit]'
})
export class OnInitDirective implements OnInit {
  @Output() onInit = new EventEmitter<void>();

  ngOnInit(): void {
    this.onInit.emit();
  }
}

Use like this:

<div (onInit)="doSomeStuff(someValue)"></div>
Dan King
  • 3,412
  • 5
  • 24
  • 23