I am trying to add component dynamically into a child component from the parent component. I have injected the child component so that I can call its method in order to add component into the child component. Here is how I am doing it:
test.ts
import {Component, DynamicComponentLoader,ElementRef,AfterViewInit} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {ChildComponent} from './child_test.ts';
#Component({
selector: 'app',
template: '<div #element></div>',
providers: [ChildComponent,ElementRef]
})
class AppComponent implements AfterViewInit{
constructor(private _loader: DynamicComponentLoader, private _elementRef: ElementRef, private _childComponent: ChildComponent) {
}
ngAfterViewInit(){
this._loader.loadIntoLocation(ChildComponent,this._elementRef,'element');
this._childComponent.addElement();
}
}
bootstrap(AppComponent);
child_test.ts
import {Component, ElementRef,DynamicComponentLoader} from 'angular2/core';
#Component({
selector: 'element',
template: '<div>Test Element</div>'
})
export class MyElement{}
#Component({
selector: "child-component",
template: "<div>Child Component</div>"
})
export class ChildComponent{
constructor(public _elementRef: ElementRef,private _loader: DynamicComponentLoader){}
public addElement(){
this._loader.loadNextToLocation(MyElement,this._elementRef);
}
};
When execute the test.ts I get the following error:
TypeError: Cannot read property 'getViewContainerRef' of undefined
Not sure if I fully understand your question but I think this is what you want
ngAfterViewInit(){
this._loader.loadIntoLocation(ChildComponent,this._elementRef,'element')
.then(childComponent => childComponent.instance.addElement());
}
this._loader.loadIntoLocation(...) returns a Promise that completes with a reference to the added element.
Related
When I use #ViewChild I get the error that the component is not defined.
When I use #ViewChildren I get the error that the function from that component is not a function.
I am new to using child components in Angular so I'm not sure why it's doing this when I do have the child component defined in the parent component and when it's clearly a function in the child component.
I don't want to have to define every function from the child in the parent or else what's even the point of using a separate component.
Child Component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-mood',
templateUrl: './mood.component.html',
styleUrls: ['./mood.component.css']
})
export class MoodComponent implements OnInit {
moodColors = ['red', 'orange', 'grey', 'yellow', 'green'];
constructor() { }
ngOnInit(): void {
}
chooseMood() {
alert(this.moodColors);
}
}
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood is undefined")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChild('mood') mood: MoodComponent = new MoodComponent;
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood.chooseMood is not a function")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChildren('mood') mood: MoodComponent = new MoodComponent;
Parent View
<h2 (click)="mood.chooseMood()"></h2>
You don't explicitly initialize view children via new.
Just use:
#ViewChild('mood') mood : MoodComponent;
If that doesn't work post a Stackblitz example which I can edit to resolve the issue.
Also, using ViewChild is more of an exception in Angular, and your use of it points to a probable design issue. More likely you child component should emit via an Output to the parent.
Regarding outputs, you can do something like this - though it is hard to give a precise answer without deeper knowledge of what you are trying to achieve:
export class MoodComponent implements OnInit {
#Input() moodId: string;
#Output() chooseMood = new EventEmitter<string>();
moodClicked(){
this.chooseMood.emit(moodId);
}
}
export class CalendarComponent implements OnInit {
moodChosen(string: moodId){
console.log(moodId);
}
}
// Calendar template:
<app-mood
moodId="happy"
(chooseMood)="moodChosen($event)"
></app-mood>
1 - you have to use this code
#ViewChild('mood') mood : MoodComponent;
when you are using #ViewChildren it will return list of items with the 'mood' name then you have to use this code
mood.first.chooseMood() ;
its better use ViewChildren when there is ngIf in your element
2- no need new keyword for initialize mood variable
it would be fill after ngOnInit life cycle fires
I've created a service that I use in 2 separate components from the same module.
The service has a few imports and is somewhat normal.
import { CdkPortal } from "#angular/cdk/portal";
import { ElementRef, Injectable } from "#angular/core";
import { Subject, Subscription } from "rxjs";
#Injectable()
export class TestService {...}
I've added the service into the module into the providers array as well as both components where the service is needed.
The first component uses the service with no issue.
import { FlyoutService } from "../flyout.service";
#Component({
selector: "test-uno",
templateUrl: "./test-uno.component.html",
styleUrls: ["./test-uno.component.scss"],
})
export class TestUnoComponent implements OnInit, OnDestroy, AfterViewInit {
constructor(private flyoutService: FlyoutService) {...}
}
The second component, that uses the service in what I think is the same way, fails.
import { FlyoutService } from "./../flyout.service";
#Component({
selector: "test-dos",
templateUrl: "./test-dos.component.html",
styleUrls: ["./test-dos.component.scss"],
animations: [...],
})
export class TestDosComponent implements OnInit, OnChanges, OnDestroy {
constructor(private flyoutService: FlyoutService) { }
}
The second component fails in the browser with the error:
Export of name 'CdkPortal' not found.
This only happens when buildOptimizer is set to true on the build. Any idea what's going on?
I have created a component called Parent and Child. I want to display all the UI of ChildComponent to my ParentComponent.
child.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
testContent = 'child component content...';
constructor() { }
ngOnInit() {
}
}
child.component.html
<p>{{testContent}}</p>
parent.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss'],
directives: [ChildComponent]
})
export class AdminComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
parent.component.html
<div>
Lorem ipsum
<app-child></app-child>
</div>
I want to display the contents of child component inside parent component. However, I am encountering an error in Angular 4
"Object literal may only specify known properties, and 'directives'
does not exist in type 'Component'."
Do you have any idea what is the alternative property to add child component?
Remove directives from the parent component and add the child component to the module declarations array where the parent component lives.
Directives and pipes in #component are deprecated since angular RC6. Just remove it from the component.
#Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss']
})
I am trying to pass a simple string object from a parent component to a sub-child component. I have tried doing it the following way:
parent.ts
import {Component} from 'angular2/core';
import {Router,ROUTER_DIRECTIVES,ROUTER_PROVIDERS,RouteConfig} from 'angular2/router';
import {ChildCmp} from "./child";
import {bootstrap} from 'angular2/platform/browser';
#Component({
selector: 'app',
template:`
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
export class ParentCmp{
public data = "Some data from parent.";
constructor (private _router:Router){
var config = [];
if(!this._router.registry.hasRoute("Child",ParentCmp))
config.push({path: "/child/...",component:ChildCmp,name: 'Child',useAsDefault:true, data: {"data": this.data}});
this._router.config(config);
}
}
bootstrap(ParentCmp,[
ROUTER_PROVIDERS
]);
child.ts
import {Component} from 'angular2/core';
import {RouteData,Router,ROUTER_DIRECTIVES,RouteConfig} from 'angular2/router';
import {SubChildCmp} from "./sub_child";
#Component({
selector: 'child',
template: `<router-outlet></router-outlet>`,
directives: [ROUTER_DIRECTIVES]
})
#RouteConfig([
])
export class ChildCmp{
public data:Object;
constructor(private _data:RouteData,private _router:Router){
this.data = this._data.get("data");
var config = [];
if(!this._router.registry.hasRoute("SubChild",ChildCmp))
config.push({path:"/sub_child",component: SubChildCmp,name:"SubChild", useAsDefault:true, data:{"data":this.data}});
this._router.config(config);
}
}
sub_child.ts
import {Component} from 'angular2/core';
import {RouteData} from 'angular2/router';
#Component({
selector: "sub-child",
template: `Data from parent is -->
{{data}}
`
})
export class SubChildCmp{
public data:Object;
constructor(private _data:RouteData){
this.data = this._data.get("data");
}
}
But I am getting a blank page. It looks like the routing configuration in child.ts is not being configured properly. How can I achieve this? I just want to pass some data from parent component to sub-child component. I re-produced the problem here on plunker
Usually a service is used for this use case
#Injectable
export class SharedData {
data;
}
#Component({
selector: 'app',
providers: [SharedData],
template:`
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
export class ParentCmp{
public data = "Some data from parent.";
constructor (private _router:Router, private _sharedData:SharedData){
var config = [];
if(!this._router.registry.hasRoute("Child",ParentCmp))
_sharedData.data = this.data;
}
}
}
export class SubChildCmp{
public data:Object;
constructor(_sharedData:SharedData){
this.data = _sharedData.data;
}
}
Using Observable or BehaviorSubject with subscribe() might be necessary if there are timing issues, for example when SubChildCmp reads the value before the ParentCmp has set it.
For more details see https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
I just started off with Angular2 quick start project. Have a simple application working. I added DataService class, so that the code will have separation of concern.
Initially I've added the DataService class write after the my main component of app which is MyAppComponent like below.
import {Component, View} from 'angular2/core';
import {NgFor} from 'angular2/common';
import {bootstrap} from 'angular2/platform/browser';
#Component({
'selector': 'my-app',
template: `<div *ngFor="#item of items">{{item}}</div>`,
directives: [NgFor],
providers: [DataService] //taking service as injectable
})
export class MyAppComponent {
items: Array<number>;
constructor(service: DataService) {
this.items = service.getItems(); //retrieving list to bind on the UI.
}
}
//created service, but its after the component which has meta annotation
export class DataService {
items: Array<number>;
constructor() {
this.items = [1, 2, 3, 4];
}
getItems() {
return this.items; //return the items list
}
}
bootstrap(MyAppComponent)
Above code compiles correctly, but at run-time it throws below error.
EXCEPTION: Cannot resolve all parameters for
MyAppComponent(undefined). Make sure they all have valid type or
annotations.
After 2 hours playing with the code, I shifted the DataService just above the MyAppComponent which got worked. I'm really glad that issue solved.
But I'm very curious to know that, why it wasn't working if I placed DataService class right after the class with MetaAnnotation over it?
Edit
I tried solution give by #Günter Zöchbauer like below,
import {Component, View, Inject, forwardRef} from 'angular2/core';
import {NgFor} from 'angular2/common';
import {bootstrap} from 'angular2/platform/browser';
#Component({
'selector': 'my-app',
template: `<div *ngFor="#item of items">{{item}}</div>`,
directives: [NgFor],
providers: [DataService] //tried commenting this still throws error.
})
export class MyAppComponent {
items: Array<number>;
constructor(#Inject(forwardRef(() => DataService)) service: DataService) {
this.items = service.getItems();
}
}
but still getting error in console. which looks wiered
EXCEPTION: TypeError: Cannot read property 'toString' of undefined
JavaScript doesn't hoist classes. Either use forwardRef, move DataService to it's own file or move DataService class above MyAppComponent
#Component({
'selector': 'my-app',
template: `<div *ngFor="#item of items">{{item}}</div>`,
directives: [NgFor],
providers: [forwardRef(() => DataService)] //taking service as injectable
})
export class MyAppComponent {
items: Array<number>;
constructor(#Inject(forwardRef(() => DataService)) service: DataService) {
this.items = service.getItems(); //retrieving list to bind on the UI.
}
}
See also
- Angular 2 error:
- http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html