Bindings not working in dynamically loaded component - javascript

I'm encountering a problem where if I dynamically load a component, none of the bindings in the template are working for me. As well as this the ngOnInit method is never triggered.
loadView() {
this._dcl.loadAsRoot(Injected, null, this._injector).then(component => {
console.info('Component loaded');
})
}
Dynamically loaded component
import {Component, ElementRef, OnInit} from 'angular2/core'
declare var $:any
#Component({
selector: 'tester',
template: `
<h1>Dynamically loaded component</h1>
<span>{{title}}</span>
`
})
export class Injected implements OnInit {
public title:string = "Some text"
constructor(){}
ngOnInit() {
console.info('Injected onInit');
}
}
This is my first time using dynamically loaded components so I think may be attempting to implement it incorrectly.
Here's a plunkr demonstrating the issue. Any help would be appreciated.

As Eric Martinez pointed out this is a known bug related to the use of loadAsRoot. The suggested workaround is to use loadNextToLocation or loadIntoLocation.
For me this was problematic as the component I was trying to dynamically load was a modal dialog from inside a component with fixed css positioning. I also wanted the ability to load the modal from any component and have it injected into the same position in the DOM regardless of what component it was dynamically loaded from.
My solution was to use forwardRef to inject my root AppComponent into the component which wants to dynamically load my modal.
constructor (
.........
.........
private _dcl: DynamicComponentLoader,
private _injector: Injector,
#Inject(forwardRef(() => AppComponent)) appComponent) {
this.appComponent = appComponent;
}
In my AppComponent I have a method which returns the app's ElementRef
#Component({
selector: 'app',
template: `
<router-outlet></router-outlet>
<div #modalContainer></div>
`,
directives: [RouterOutlet]
})
export class AppComponent {
constructor(public el:ElementRef) {}
getElementRef():ElementRef {
return this.el;
}
}
Back in my other component (the one that I want to dynamically load the modal from) I can now call:
this._dcl.loadIntoLocation(ModalComponent, this.appComponent.getElementRef(), 'modalContainer').then(component => {
console.log('Component loaded')
})
Perhaps this will help others with similar problems.

No need to clean component instance from DOM.
use 'componentRef' from angular2/core package to create and dispose component instance.
use show() to load the modal component at desired location and hide() to dispose the component instance before calling loadIntoLocation secondtime.
for eg:
#Component({
selector: 'app',
template: `
<router-outlet></router-outlet>
<div #modalContainer></div>
`,
directives: [RouterOutlet]
})
export class AppComponent {
private component:Promise<ComponentRef>;
constructor(public el:ElementRef) {}
getElementRef():ElementRef {
return this.el;
}
show(){
this.component = this._dcl.loadIntoLocation(ModalComponent,this.appComponent.getElementRef(), 'modalContainer').then(component => {console.log('Component loaded')})
}
hide(){
this.component.then((componentRef:ComponentRef) => {
componentRef.dispose();
return componentRef;
});
}
}

Related

Angular 2+: Child components ts variable changes but UI does not show changed value?

I have a child TestComponent component as follows:
import { Component, OnInit, Input } from '#angular/core';
import { ApiService } from '../../../api.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
constructor(private apiService: ApiService) { }
testDisplayMessage = 'No data to show';
ngOnInit() {
}
getMessage(param: string) {
this.callingTest = true;
this.apiService.getTest( param ).subscribe( data => {
this.setTestDisplayMessage( data );
this.callingTest = false;
}, err => {
console.log( JSON.stringify( err ) );
this.setTestDisplayMessage( 'Failed to get data' );
this.callingTest = false;
} );
}
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
}
}
contents of test.component.html
<p style="padding: 10px;">{{ testDisplayMessage }}</p>
Use in parent componet :
Trigger JS Code in parent component on button click,
import { TestComponent } from './test/test.component';
....
.....
#Component({
providers: [ TestComponent ],
templateUrl: 'parent.component.html'
})
export class ParentComponent implements OnInit {
...
constructor(private testComponent: TestComponent) { }
...
// Button on parent template triggers this method
getMessage() {
this.testComponent.getMessage('Hello');
}
...
}
Html tag added in parent component,
<app-test></app-test>
When I debugged above code trigger point, call to setTestDisplayMessage() happens the field testDisplayMessage in TestComponent gets changed but UI shows the old message 'No data to show', why is the message on change does not reflect on UI template? Or this is not the way it is supposed to get used? Shall I use #Input
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
Use #ViewChild()
In html file:
<app-test #childComp></app-test>
In parent component.ts file
import { Component, OnInit, ViewChild } from '#angular/core';
....
.....
#Component( {
templateUrl: 'parent.component.html'
} )
export class ParentComponent implements OnInit {
#viewChild('childComp') childComp: any;
constructor() { }
...
// Button on parent template triggers this method
getMessage() {
this.childComp.getMessage('Hello');
}
...
}
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
definitely use #Input() but on set method
#Input()
set someProperty(value) {
// do some code
}
now every time you pass new value here, code will run
basically, your approach is wrong, please use Input() or Services to share data between components.
however, if you want to make ur code work, the below may work
import change detector
constructor(private cdRef: ChangeDetectorRef) {
}
note: import reference ->
import { ChangeDetectorRef } from '#angular/core';
execute detect change after the value is updated
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
this.cdRef.detectChanges();
}
I hope this helps

Making a reusable angular2 component that can be used anywhere on the a site

Use Case: When making asynchronous calls, I want to show some sort of a processing screen so that end users knows something is happening rather than just staring at the screen. Since I have multiple places throughout the site where I want to use this, I figured making it a component at the "global" level is the best approach.
Problem: Being slightly new to angular2, I'm not getting if this is a problem of it being outside the directory in which the main component exists and the OverlayComponent being in another location or if I'm just all together doing it wrong. I can get the component to work fine but I need to be able to call functions to hide/destroy the component and also display the component. I have tried making it a service but that didn't get me any further so I'm back to square one. Essentially my question revolves around building a reusable component that has methods to hide/show itself when invoked from whatever component it's being called from.
Below is my current code:
Assume OverlayComponent.html is at /public/app/templates/mysite.overlay.component.html
Assume OverlayComponent.ts is at /public/app/ts/app.mysite.overlay.component
Assume mysite.tracker.component is at \public\app\ts\pages\Tracker\mysite.tracker.component.ts
OverlayComponent.html
<div class="overlay-component-container">
<div class="overlay-component" (overlay)="onShowOverlay($event)">
<div>{{processingMessage}}</div>
<div>
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
</div>
</div>
OverlayComponent.ts
import { Component } from '#angular/core';
#Component({
selector: 'overlay-component',
templateUrl: '/public/app/templates/mysite.overlay.component.html',
styleUrls: ['public/app/scss/overlay.css']
})
export class OverlayComponent {
onShowOverlay(e) {
$('.overlay-component').fadeIn(1000);
}
hideOverlay(e) {
$('.overlay-component').fadeOut(1000);
}
}
TrackerComponent.ts
import { Component, Output, OnInit, EventEmitter } from '#angular/core';
import { Http } from '#angular/http';
import { TrackerService } from './Tracker.service';
import { MenuCollection } from "./MenuCollection";
import { Menu } from "./Menu";
#Component({
moduleId: module.id,
selector: 'tracker-component',
templateUrl: '/public/app/templates/pages/tracker/mysite.tracker.component.html',
styleUrls: ['../../../scss/pages/racker/tracker.css'],
providers: [TrackerService]
})
export class TrackerComponent implements OnInit{
MenuCollection: MenuCollection;
#Output()
overlay: EventEmitter<any> = new EventEmitter();
constructor(private http: Http, private TrackerService: TrackerService) {
let c = confirm("test");
if (c) {
this.onShowOverlay();
}
}
ngOnInit(): void {
this.MenuCollection = new MenuCollection();
this.MenuCollection.activeMenu = new Menu('Active Menu', []);
this.TrackerService.getTrackerData().then(Tracker => {
this.MenuCollection = Tracker;
this.MenuCollection.activeMenu = this.MenuCollection.liveMenu;
console.log(this.MenuCollection);
},
error => {
alert('error');
})
}
onShowOverlay() { //This doesn't seem to 'emit' and trigger my overlay function
this.overlay.emit('test');
}
}
At a high level, all I'm wanting to do is invoke a components function from another component. Thanks in advance for any helpful input
You can use the #ContentChild annotation to accomplish this:
import { Component, ContentChild } from '#angular/core';
class ChildComponent {
// Implementation
}
// this component's template has an instance of ChildComponent
class ParentComponent {
#ContentChild(ChildComponent) child: ChildComponent;
ngAfterContentInit() {
// Do stuff with this.child
}
}
For more examples, check out the #ContentChildren documentation.

In angular2, are there any methods just like $compile()? [duplicate]

I want to manually compile some HTML containing directives. What is the equivalent of $compile in Angular 2?
For example, in Angular 1, I could dynamically compile a fragment of HTML and append it to the DOM:
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
Angular 2.3.0 (2016-12-07)
To get all the details check:
How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
To see that in action:
observe a working plunker (working with 2.3.0+)
The principals:
1) Create Template
2) Create Component
3) Create Module
4) Compile Module
5) Create (and cache) ComponentFactory
6) use Target to create an Instance of it
A quick overview how to create a Component
createNewComponent (tmpl:string) {
#Component({
selector: 'dynamic-component',
template: tmpl,
})
class CustomDynamicComponent implements IHaveDynamicData {
#Input() public entity: any;
};
// a component for this particular template
return CustomDynamicComponent;
}
A way how to inject component into NgModule
createComponentModule (componentType: any) {
#NgModule({
imports: [
PartsModule, // there are 'text-editor', 'string-editor'...
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
A code snippet how to create a ComponentFactory (and cache it)
public createComponentFactory(template: string)
: Promise<ComponentFactory<IHaveDynamicData>> {
let factory = this._cacheOfFactories[template];
if (factory) {
console.log("Module and Type are returned from cache")
return new Promise((resolve) => {
resolve(factory);
});
}
// unknown template ... let's create a Type for it
let type = this.createNewComponent(template);
let module = this.createComponentModule(type);
return new Promise((resolve) => {
this.compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories) =>
{
factory = _.find(moduleWithFactories.componentFactories
, { componentType: type });
this._cacheOfFactories[template] = factory;
resolve(factory);
});
});
}
A code snippet how to use the above result
// here we get Factory (just compiled or from cache)
this.typeBuilder
.createComponentFactory(template)
.then((factory: ComponentFactory<IHaveDynamicData>) =>
{
// Target will instantiate and inject component (we'll keep reference to it)
this.componentRef = this
.dynamicComponentTarget
.createComponent(factory);
// let's inject #Inputs to component instance
let component = this.componentRef.instance;
component.entity = this.entity;
//...
});
The full description with all the details read here, or observe working example
.
.
OBSOLETE - Angular 2.0 RC5 related (RC5 only)
to see previous solutions for previous RC versions, please, search through the history of this post
Note: As #BennyBottema mentions in a comment, DynamicComponentLoader is now deprecated, hence so is this answer.
Angular2 doesn't have any $compile equivalent. You can use DynamicComoponentLoader and hack with ES6 classes to compile your code dynamically (see this plunk):
import {Component, DynamicComponentLoader, ElementRef, OnInit} from 'angular2/core'
function compileToComponent(template, directives) {
#Component({
selector: 'fake',
template , directives
})
class FakeComponent {};
return FakeComponent;
}
#Component({
selector: 'hello',
template: '<h1>Hello, Angular!</h1>'
})
class Hello {}
#Component({
selector: 'my-app',
template: '<div #container></div>',
})
export class App implements OnInit {
constructor(
private loader: DynamicComponentLoader,
private elementRef: ElementRef,
) {}
ngOnInit() {} {
const someDynamicHtml = `<hello></hello><h2>${Date.now()}</h2>`;
this.loader.loadIntoLocation(
compileToComponent(someDynamicHtml, [Hello])
this.elementRef,
'container'
);
}
}
But it will work only until html parser is inside angular2 core.
Angular Version I have Used - Angular 4.2.0
Angular 4 is came up with ComponentFactoryResolver to load components at runtime. This is a kind of same implementation of $compile in Angular 1.0 which serves your need
In this below example I am loading ImageWidget component dynamically in to a DashboardTileComponent
In order to load a component you need a directive that you can apply to ng-template which will helps to place the dynamic component
WidgetHostDirective
import { Directive, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[widget-host]',
})
export class DashboardTileWidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) {
}
}
this directive injects ViewContainerRef to gain access to the view container of the element that will host the dynamically added component.
DashboardTileComponent(Place holder component to render the dynamic component)
This component accepts an input which is coming from a parent components or you can load from your service based on your implementation. This component is doing the major role to resolve the components at runtime. In this method you can also see a method named renderComponent() which ultimately loads the component name from a service and resolve with ComponentFactoryResolver and finally setting data to the dynamic component.
import { Component, Input, OnInit, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '#angular/core';
import { DashboardTileWidgetHostDirective } from './DashbardWidgetHost.Directive';
import { TileModel } from './Tile.Model';
import { WidgetComponentService } from "./WidgetComponent.Service";
#Component({
selector: 'dashboard-tile',
templateUrl: 'app/tile/DashboardTile.Template.html'
})
export class DashboardTileComponent implements OnInit {
#Input() tile: any;
#ViewChild(DashboardTileWidgetHostDirective) widgetHost: DashboardTileWidgetHostDirective;
constructor(private _componentFactoryResolver: ComponentFactoryResolver,private widgetComponentService:WidgetComponentService) {
}
ngOnInit() {
}
ngAfterViewInit() {
this.renderComponents();
}
renderComponents() {
let component=this.widgetComponentService.getComponent(this.tile.componentName);
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
let viewContainerRef = this.widgetHost.viewContainerRef;
let componentRef = viewContainerRef.createComponent(componentFactory);
(<TileModel>componentRef.instance).data = this.tile;
}
}
DashboardTileComponent.html
<div class="col-md-2 col-lg-2 col-sm-2 col-default-margin col-default">
<ng-template widget-host></ng-template>
</div>
WidgetComponentService
This is a service factory to register all the components that you want to resolve dynamically
import { Injectable } from '#angular/core';
import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
#Injectable()
export class WidgetComponentService {
getComponent(componentName:string) {
if(componentName==="ImageTextWidgetComponent"){
return ImageTextWidgetComponent
}
}
}
ImageTextWidgetComponent(component we are loading at runtime)
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'dashboard-imagetextwidget',
templateUrl: 'app/templates/ImageTextWidget.html'
})
export class ImageTextWidgetComponent implements OnInit {
#Input() data: any;
constructor() { }
ngOnInit() { }
}
Add Finally add this ImageTextWidgetComponent in to your app module as entryComponent
#NgModule({
imports: [BrowserModule],
providers: [WidgetComponentService],
declarations: [
MainApplicationComponent,
DashboardHostComponent,
DashboardGroupComponent,
DashboardTileComponent,
DashboardTileWidgetHostDirective,
ImageTextWidgetComponent
],
exports: [],
entryComponents: [ImageTextWidgetComponent],
bootstrap: [MainApplicationComponent]
})
export class DashboardModule {
constructor() {
}
}
TileModel
export interface TileModel {
data: any;
}
Orginal Reference from my blog
Official Documentation
Download Sample Source Code
this npm package made it easier for me:
https://www.npmjs.com/package/ngx-dynamic-template
usage:
<ng-template dynamic-template
[template]="'some value:{{param1}}, and some component <lazy-component></lazy-component>'"
[context]="{param1:'value1'}"
[extraModules]="[someDynamicModule]"></ng-template>
In order to dinamically create an instance of a component and attach it to your DOM you can use the following script and should work in Angular RC:
html template:
<div>
<div id="container"></div>
<button (click)="viewMeteo()">Meteo</button>
<button (click)="viewStats()">Stats</button>
</div>
Loader component
import { Component, DynamicComponentLoader, ElementRef, Injector } from '#angular/core';
import { WidgetMeteoComponent } from './widget-meteo';
import { WidgetStatComponent } from './widget-stat';
#Component({
moduleId: module.id,
selector: 'widget-loader',
templateUrl: 'widget-loader.html',
})
export class WidgetLoaderComponent {
constructor( elementRef: ElementRef,
public dcl:DynamicComponentLoader,
public injector: Injector) { }
viewMeteo() {
this.dcl.loadAsRoot(WidgetMeteoComponent, '#container', this.injector);
}
viewStats() {
this.dcl.loadAsRoot(WidgetStatComponent, '#container', this.injector);
}
}
Angular TypeScript/ES6 (Angular 2+)
Works with AOT + JIT at once together.
I created how to use it here:
https://github.com/patrikx3/angular-compile
npm install p3x-angular-compile
Component: Should have a context and some html data...
Html:
<div [p3x-compile]="data" [p3x-compile-context]="ctx">loading ...</div>
You can see the component, that allow to compile simple dynamic Angular components https://www.npmjs.com/package/#codehint-ng/html-compiler
I know this issue is old, but I spent weeks trying to figure out how to make this work with AOT enabled. I was able to compile an object but never able to execute existing components. Well I finally decided to change tact, as I was't looking to compile code so much as execute a custom template. My thought was to add the html which anyone can do and loop though the existing factories. In doing so I can search for the element/attribute/etc. names and execute the component on that HTMLElement. I was able to get it working and figured I should share this to save someone else the immense amount of time I wasted on it.
#Component({
selector: "compile",
template: "",
inputs: ["html"]
})
export class CompileHtmlComponent implements OnDestroy {
constructor(
private content: ViewContainerRef,
private injector: Injector,
private ngModRef: NgModuleRef<any>
) { }
ngOnDestroy() {
this.DestroyComponents();
}
private _ComponentRefCollection: any[] = null;
private _Html: string;
get Html(): string {
return this._Html;
}
#Input("html") set Html(val: string) {
// recompile when the html value is set
this._Html = (val || "") + "";
this.TemplateHTMLCompile(this._Html);
}
private DestroyComponents() { // we need to remove the components we compiled
if (this._ComponentRefCollection) {
this._ComponentRefCollection.forEach((c) => {
c.destroy();
});
}
this._ComponentRefCollection = new Array();
}
private TemplateHTMLCompile(html) {
this.DestroyComponents();
this.content.element.nativeElement.innerHTML = html;
var ref = this.content.element.nativeElement;
var factories = (this.ngModRef.componentFactoryResolver as any)._factories;
// here we loop though the factories, find the element based on the selector
factories.forEach((comp: ComponentFactory<unknown>) => {
var list = ref.querySelectorAll(comp.selector);
list.forEach((item) => {
var parent = item.parentNode;
var next = item.nextSibling;
var ngContentNodes: any[][] = new Array(); // this is for the viewchild/viewchildren of this object
comp.ngContentSelectors.forEach((sel) => {
var ngContentList: any[] = new Array();
if (sel == "*") // all children;
{
item.childNodes.forEach((c) => {
ngContentList.push(c);
});
}
else {
var selList = item.querySelectorAll(sel);
selList.forEach((l) => {
ngContentList.push(l);
});
}
ngContentNodes.push(ngContentList);
});
// here is where we compile the factory based on the node we have
let component = comp.create(this.injector, ngContentNodes, item, this.ngModRef);
this._ComponentRefCollection.push(component); // save for our destroy call
// we need to move the newly compiled element, as it was appended to this components html
if (next) parent.insertBefore(component.location.nativeElement, next);
else parent.appendChild(component.location.nativeElement);
component.hostView.detectChanges(); // tell the component to detectchanges
});
});
}
}
If you want to inject html code use directive
<div [innerHtml]="htmlVar"></div>
If you want to load whole component in some place, use DynamicComponentLoader:
https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html

Load dynamic component created on the fly in angular2 final

Previously with DynamicComponentLoader I was able to write like this:
import {Directive, Component, ViewContainerRef, DynamicComponentLoader} from '#angular/core';
#Directive({
selector: '[some-directive]'
})
export class SomeDirective {
costructor(dcl: DynamicComponentLoader, viewContainerRef: ViewContainerRef) {
// fetch template from the server
fetch(...).then((template) => {
#Component({
selector: 'div[some-relatively-unique-attribute-name]',
template: template
})
class AdHocComponent {}
dcl.loadNextToLocation(AdHocComponent, viewContainerRef).then(() => {
console.log('success');
});
});
}
}
Now with angular2 final and NgModules I see examples like this: http://plnkr.co/edit/P0spNzu8JbQad2aKACsX?p=info
(Discussed here https://github.com/angular/angular/issues/10735)
To dynamically load a HelloComponent but it requires the HelloComponent to be declared up front when the root NgModule is being created.
How can I load an ad-hoc created component into my view?
I found this: http://plnkr.co/edit/wh4VJG?p=preview
But it is an insane amount of code to achieve a simple task like that.
This might be that what you're looking for:
export class App {
#ViewChild('placeholder', {read: ViewContainerRef}) viewContainerRef;
constructor(private compiler: Compiler) {}
addItem () {
#NgModule({declarations: [HelloComponent]})
class DynamicModule {}
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({moduleFactory, componentFactories}) => {
const compFactory = componentFactories
.find(x => x.componentType === HelloComponent);
const cmpRef = this.viewContainerRef.createComponent(compFactory, 0);
});
}
}
See also live Plunker
Related question:
Angular2 RC6 - Dynamically load component from module

Angular 2, How to pass options from parent component to child component?

I have been looking for a solution for this for a while. I have tried a bunch of different things from #Input, #Query, dynamicContentLoader, #Inject, #Inject(forwardRef(() but haven't been able to figure this out yet.
My Example Structure:
parent.ts
import {Component} from 'angular2/core';
#Component({
selector : 'my-app',
directives : [Child],
template : `<parent-component></parent-component>`
})
export class Parent
{
private options:any;
constructor()
{
this.options = {parentThing:true};
}
}
child.ts
import {Component} from 'angular2/core';
#Component({
selector : 'parent-component',
template : `<child-component></child-component>`
})
export class Child
{
constructor(private options:any) <- maybe I can pass them in here somehow?
{
// what I am trying to do is get options from
// the parent component at this point
// but I am not sure how to do this with Angular 2
console.log(options) <- this should come from the parent
// how can I get parent options here?
// {parentThing:true}
}
}
This is my current HTML output in the DOM, so this part is working as expected
<parent-component>
<child-component></child-component>
</parent-component>
Question Summarized:
How can I pass options from a parent component to a child component and have those options available in the child constructor?
Parent to child is the simplest form of all but it's not available in the constructor, only in ngOnInit() (or later).
This only requires an #Input() someField; in the child component and using binding this can be passed from parent to children. Updates in the parent are updated in the child (not the other direction)
#Component({
selector: 'child-component',
template'<div>someValueFromParent: {{someValueFromParent}}</div>'
})
class ChildComponent {
#Input() someValueFromParent:string;
ngOnInit() {
console.log(this.someValue);
}
}
#Component({
selector: 'parent-component',
template: '<child-component [someValueFromParent]="someValue"></child-component>'
})
class ParentComponent {
someValue:string = 'abc';
}
to have it available in the constructor use a shared service. A shared service is injected into the constructor of both components. For injection to work the service needs to be registered in the parent component or above but not in the child. This way both get the same instance.
Set a value in the parent and read it in the client.
#Injectable()
class SharedService {
someValue:string;
}
#Component({
selector: 'child-component',
template: '<div>someValueFromParent: {{someValueFromParent}}</div>'
})
class ChildComponent {
constructor(private sharedService:SharedService) {
this.someValue = sharedService.someValue;
}
someValue:string = 'abc';
}
#Component({
selector: 'parent-component',
providers: [SharedService],
template: '<child-component></child-component>'
})
class ParentComponent {
constructor(private sharedService:SharedService) {
sharedService.someValue = this.someValue;
}
someValue:string = 'abc';
}
update
There is not much difference. For DI only the constructor can be used. If you want something injected it has to be through the constructor. ngOnInit() is called by Angular when additional initialization has taken place (like bindings being processed). For example if you make a network call it doesn't matter if you do it in the constructor on in ngOnInit because the call to the server is scheduled for later anyway (async). When the current sync task is completed, JavaScript looks for the next scheduled task and processes it (and so on). Therefore it's probably so that the server call initiated in the constructor is actually sent after ngOnInit() anyway no matter where you place it.
You could use an #Input parameter:
import {Component,Input} from 'angular2/core';
#Component({
selector : 'parent-component',
template : `<child-component></child-component>`
})
export class Child {
#Input()
options:any;
ngOnInit() {
console.log(this.options);
}
}
Notice that the value of options is available in the ngOnInit and not in the constructor. Have a look at the component lifecycle hooks for more details:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
And provides the options as decribed below:
import {Component} from 'angular2/core';
#Component({
selector : 'my-app',
directives : [Child],
template : `<parent-component [options]="options"></parent-component>`
})
export class Parent {
private options:any;
constructor() {
this.options = {parentThing:true};
}
}
If you want to implement custom events: child triggers an event and the parent register to be notified. Use #Output.

Categories

Resources