I am new to angular2. I have a requirement to call a function when a template loads/initializes. I know how to do this in angular1.x., but I am not able to find out how it can be done in angular-2.
This is how I tried in angular1.x
In html
<div ng-init="getItems()">
//some logic to get my items
</div>
In controller
getItems = function(){
console.log('in the getitems function call');
//logic to get my items from db/localStorage
}
This is how I used ng-init in angular1.x, but there is no ng-init in angular-2?Please help me on this issue.
#Component({
...
})
class MyComponent {
constructor() {
// when component class instance is created
}
ngOnChanges(...) {
// when inputs are updated
}
ngOnInit() {
// after `ngOnChanges()` was called the first time
}
ngAfterViewInit() {
// after the view was created
}
ngAfterContentInit() {
// after content was projected
}
}
See also https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-overview for the full list
Check lifecycle events of a component https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html . From what you are saying you probably needs ngAfterViewInit
In angular2 you can use component phase ngOnInit it is equal to on-init in angularJS. Here is more information about lifecycle in angular.
Example:
export class PeekABoo implements OnInit {
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() {
this.logIt(`OnInit`);
}
protected logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
Related
The code starts with an initial value in product variable, which is setted into sessionStorage. When i trigger the side-panel (child component), this receive the product.name from params in url, then this component searchs in sessionStorage and updates the product.amount value (and set it to sessionStorage).
The parent component function that i'm trying to invoke from the child component is getProductStatus(); When i update the product.amount value in the side-panel i need to update also the product object in parent component at the same time. This is what i've been trying, Thanks in advance.
Code:
https://stackblitz.com/edit/angular-ivy-npo4z7?file=src%2Fapp%2Fapp.component.html
export class AppComponent {
product: any;
productReturned: any;
constructor() {
this.product = {
name: 'foo',
amount: 1
};
}
ngOnInit() {
this.getProductStatus();
}
getProductStatus(): void {
this.productReturned = this.getStorage();
if (this.productReturned) {
this.product = JSON.parse(this.productReturned);
} else {
this.setStorage();
}
}
setStorage(): void {
sessionStorage.setItem(this.product.name, JSON.stringify(this.product));
}
getStorage() {
return sessionStorage.getItem(this.product.name);
}
reset() {
sessionStorage.clear();
window.location.reload();
}
}
You have two options for data sharing in this case. If you only need the data in your parent component:
In child.component.ts:
#Output() someEvent = new EventEmitter
someFunction(): void {
this.someEvent.emit('Some data...')
}
In parent template:
<app-child (someEvent)="handleSomeEvent($event)"></app-child>
In parent.component.ts:
handleSomeEvent(event: any): void {
// Do something (with the event data) or call any functions in the component
}
If you might need the data in another component aswell, you could make a service bound to the root of the application with a Subject to subscibe to in any unrelated component wherever in your application.
Service:
#Injectable()
export class DataService {
private _data = new BehaviorSubject<SnapshotSelection>(new Data());
private dataStore: { data: any }
get data() {
return this.dataStore.asObservable();
}
updatedDataSelection(data: Data){
this.dataStore.data.push(data);
}
}
Just pass the service in both constructors of receiving and outgoing component.
In ngOnInit() on receiving side:
subscription!: Subscription
...
dataService.data.subscribe(data => {
// Do something when data changes
})
...
ngOnDestroy() {
this.subscription.unsubscribe()
}
Then just use updatedDataSelection() where the changes originate.
I documented on all types of data sharing between components here:
https://github.com/H3AR7B3A7/EarlyAngularProjects/tree/master/dataSharing
For an example on the data service:
https://github.com/H3AR7B3A7/EarlyAngularProjects/tree/master/dataService
Lets say I have 2 components, aComponent and bComponent. I have them redered inside the AppComponent
<app-a>
<app-b>
And I have service myService that has method .trigger().
What I want is to show only aComponent, but whenever I call myService.trigger() from another part of code, it would switch and show bComponent. That's perfect implementation that I can't reach.
Question is: Is it possible to do so? And if not what is the best closest solution.
The only working solution I got:
I added .trigger() inside AppComponent
export class AppComponent {
title = 'spa';
show: boolean = false;
trigger() {
this.show = true;
}
}
And rendered components like so:
<div *ngIf="!show; else show">
<app-a></app-a>
</div>
<ng-template #show>
<app-b></app-b>
</ng-template>
Then whenever I want to trigger switching, I add instance of the app to the constructor and call it's method:
export class AnotherComponent implements OnInit {
constructor(
private app: AppComponent
) {}
ngOnInit(): void {
this.app.trigger();
}
}
Even though it's working pretty good, I myself see that it's a dirty solution. Components are not intended to be used inside another components, but Services are.
You can use Subject from rxjs library for that.
In your service file:
// a-service.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable({ providedIn: 'root' })
export class AService {
private subject = new Subject<any>();
trigger(state: boolean) {
this.subject.next(state);
}
getTrigger(): Subject<any> {
return this.subject;
}
}
and in your AppComponent:
// app.component.ts
...
private show = false;
constructor (private aService: AService) { }
ngOnInit() {
this.aService.getTrigger().subscribe(state => {
this.show = state;
});
}
the template can be as you provided - it's fine:
<div *ngIf="!show; else show">
<app-a></app-a>
</div>
<ng-template #show>
<app-b></app-b>
</ng-template>
And if you want to trigger from another component, you do it like this:
// another.component.ts
...
constructor (private aService: AService) { }
ngOnInit() {
this.aService.trigger(true);
}
One way to communicate between different components and services which aren't directly related, is via 'Subjects'.
You can try to create a subject and pass in values to it from myService.trigger(). And you can subscribe to that subject from whichever component you want to access that trigger data.
This question related to Syntactically anonymous/Arrow Function/add-hoc/factory DP functions:
I have a component which is embedded in the Html.
The component has a click event which is binded to a function. This function content depend on another component which has a reference to this component.
This is the component with the click event:
HTML:
<div (click)="doSomething()">Content.....</div> \\ Should it be with a brackets ?
In the component I just want to define the function signature:
#Component({
selector: 'app-embedded'
})
export class className
{
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) => any; //The function get a boolean parameter as input and return void or any
}
Now this is where the component is embedded:
<div >
<app-embedded #emb></app-embedded>
</div>
This is the component of the container of the embedded component, which has a reference to the embedded component:
#Component({
selector: 'app-container',
})
export class container
{
#ViewChild('emb') private emb: ElementRef;
booleanParam : booelan;
constructor()
{
emb.doSomething = containerFunction(true);
}
containerFunction(booleanParam : boolean)
{
// do something in this context
}
}
The idea is that this embedded component is embedded in many other containers and whenever the click event triggered a function that was set in the doSomething function variable should be executed.
What changes in the code I need to do in order to accomplish this ?
The best way i see of doing this would be to simply use an event emitter and capture the event on the other side? so embedded would have this:
#Component({
selector: 'app-embedded'
})
export class className
{
#Output()
public something: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
this.something.emit(booleanparams);
}; //The function get a boolean parameter as input and return void or any
}
Then where it is called:
<div >
<app-embedded #emb (something)="doSomething($event)"></app-embedded>
</div>
Other solution that would allow a return
#Component({
selector: 'app-embedded'
})
export class className
{
#Input()
public somethingFunc: (boolean)=>any;
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
let w_potato = this.somethingFunc(booleanparams);
//Do whatever you want with w_potato
}; //The function get a boolean parameter as input and return void or any
}
in this case the view would be
<div >
<app-embedded #emb [somethingFunc]="doSomething"></app-embedded>
</div>
I hope this helps! Passing the function or emitting an event will be much more angular than trying to modify an instance of a component. On top of that, a constructor is only called once when Angular starts up so #emb at that time will not be defined to be anything. If you wanted to do it that way you would have to bind yourself in something ngAfterViewInit.
But again, I think that passing it through attributes will be much more angular looking.
Good Luck let me know if this doesn't suit your answer.
Following what is documented here: Dynamic Component Loader.
I want to know how is it possible to handle the data inside this HeroJobAdComponent class:
import { Component, Input } from '#angular/core';
import { AdComponent } from './ad.component';
#Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
</div>
`
})
export class HeroJobAdComponent implements AdComponent {
#Input() data: any;
}
As you can see, data is the object holding the data received. I want to be able to define a constructor for my HeroJobAdComponent class but if I do, the object data is undefined inside my constructor. I tried using ngOnChange instead which supposedly executes once input is changed from undefined to defined but it also did not execute at all.
Can someone please explain first why is the object undefined even though the data is defined in my main component calling it, and what's the workaround for this issue?
This is the constructor I am using:
constructor()
{
this.values = this.data.values;
this.spec_name = this.data.spec_name;
}
if you want to use any operation when you receive data in your component , you can use setter
export class HeroJobAdComponent implements AdComponent {
_data;
#Input() set data (data: any){
//operation on data goes here
this._data=data
};
get data() {
return this._data;
}
}
I am using a javascript Object that has a callback. Once the callback is fired I want to call a function inside an Angular2 component.
example
HTML file.
var run = new Hello('callbackfunction');
function callbackfunction(){
// how to call the function **runThisFunctionFromOutside**
}
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'js/app': {defaultExtension: 'ts'}}
});
System.import('js/app/main')
.then(null, console.error.bind(console));
</script>
My App.component.ts
import {Component NgZone} from 'angular2/core';
import {GameButtonsComponent} from './buttons/game-buttons.component';
#Component({
selector: 'my-app',
template: ' blblb'
})
export class AppComponent {
constructor(private _ngZone: NgZone){}
ngOnInit(){
calledFromOutside() {
this._ngZone.run(() => {
this.runThisFunctionFromOutside();
});
}
}
runThisFunctionFromOutside(){
console.log("run");
}
How can i call the function runThisFunctionFromOutside which is inside App.component.ts
I basically followed this answer, but I didn't want my "outside" code to know anything about NgZone. This is app.component.ts:
import {Component, NgZone, OnInit, OnDestroy} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private ngZone: NgZone) {}
ngOnInit() {
window.my = window.my || {};
window.my.namespace = window.my.namespace || {};
window.my.namespace.publicFunc = this.publicFunc.bind(this);
}
ngOnDestroy() {
window.my.namespace.publicFunc = null;
}
publicFunc() {
this.ngZone.run(() => this.privateFunc());
}
privateFunc() {
// do private stuff
}
}
I also had to add a definition for TypeScript to extend the window object. I put this in typings.d.ts:
interface Window { my: any; }
Calling the function from the console is now as simple as:
my.namespace.publicFunc()
See also How do expose angular 2 methods publicly?
When the component is constucted make it assign itself to a global variable. Then you can reference it from there and call methods.
Don't forget to use zone.run(() => { ... }) so Angular gets notified about required change detection runs.
function callbackfunction(){
// window['angularComponentRef'] might not yet be set here though
window['angularComponent'].zone.run(() => {
runThisFunctionFromOutside();
});
}
constructor(private _ngZone: NgZone){
window['angularComponentRef'] = {component: this, zone: _ngZone};
}
ngOnDestroy() {
window.angularComponent = null;
}
Plunker example1
In the browser console you have to switch from <topframe> to plunkerPreviewTarget.... because Plunker executes the code in an iFrame. Then run
window['angularComponentRef'].zone.run(() => {window['angularComponentRef'].component.callFromOutside('1');})
or
window.angularComponentRef.zone.run(() => {window.angularComponentRef.componentFn('2');})
An alternative approach
would be to dispatch events outside Angular and listen to them in Angular like explained in Angular 2 - communication of typescript functions with external js libraries
Plunker example2 (from the comments)
Below is a solution.
function callbackfunction(){
window.angularComponent.runThisFunctionFromOutside();
}
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'js/app': {defaultExtension: 'ts'}}
});
System.import('js/app/main')
.then(null, console.error.bind(console));
</script>
My App.component.ts
import {Component NgZone} from 'angular2/core';
import {GameButtonsComponent} from './buttons/game-buttons.component';
#Component({
selector: 'my-app',
template: ' blblb'
})
export class AppComponent {
constructor(private _ngZone: NgZone){
window.angularComponent = {runThisFunctionFromOutside: this.runThisFunctionFromOutside, zone: _ngZone};
}
runThisFunctionFromOutside(){
console.log("run");
}
}
An other approach without using global variables is to use pass a control object and bind its properties to the variables and methods to expose.
export class MyComponentToControlFromOutside implements OnChanges {
#Input() // object to bind to internal methods
control: {
openDialog,
closeDialog
};
ngOnChanges() {
if (this.control) {
// bind control methods to internal methods
this.control.openDialog = this.internalOpenDialog.bind(this);
this.control.closeDialog = this.internalCloseDialog;
}
}
internalOpenDialog(): Observable<boolean> {
// ...
}
internalCloseDialog(result: boolean) {
// ...
}
}
export class MyHostComponent {
controlObject= {};
}
<my-component-to-control [control]="controlObject"></my-component-to-control>
<a (click)="controlObject.open()">Call open method</a>
I had a similar situation when using the callback 'eventClick' of the fullCalendar library, whose callbacks are returning from outside the angular zone, causing my application to have partial and unreliable effects. I was able to combine the zone approach and a closure reference to the component as seen below in order to raise an output event. Once I started executing the event inside of the zone.run() method the event and it's effects were once again predictable and picked up by angular change detection. Hope this helps someone.
constructor(public zone: NgZone) { // code removed for clarity
}
ngOnInit() {
this.configureCalendar();
}
private configureCalendar() {
// FullCalendar settings
this.uiConfig = {
calendar: { // code removed for clarity
}
};
this.uiConfig.calendar.eventClick = this.onEventClick();
}
private onEventClick() {
const vm = this;
return function (event, element, view) {
vm.zone.run(() => {
vm.onSequenceSelected.emit(event.sequenceSource);
});
return false;
};
}
Just adding to #Dave Kennedy:
Calling the function from the console is now as simple as:
my.namespace.publicFunc()
1) If we try to access our component's public method from a different domain you will get caught into CORS issue (the cross origin problem, can be solved if both server and client code resides in same machine).
2) if you were to call this method from server using javascript, you will have to use window.opener.my.namespace.publicFunc() instead of window.my.namespace.publicFunc():
window.opener.my.namespace.publicFunc();