angular2 need to access the child value in parent class - angular2-forms

Here, I need to access the child value in parent class.
But I am not able to get it. If I am using directives it showing error.
Can someone please help me in displaying the child values in parent component and how the child value can be validated in reactive form?
parent.html :
<browser [template]="coreActivity" (onselect)="onselect($event)" ></browser>
parent.ts :
onselect(select: any)
{
console.log(select.value);
}
child.html :
<md-grid-list cols="3" rowHeight="100px">
<md-grid-tile *ngFor="let core of template" (click)="selectcore(core)">
{{core.value}}
</md-grid-tile>
</md-grid-list>
child.ts :
#Component({
selector: 'template-browser',
templateUrl: './template-browser.component.html',
styleUrls: ['./template-browser.component.css']
})
export class TemplateBrowserComponent implements OnInit {
#Input() template;
#Output() notify: EventEmitter<string> = new EventEmitter<string>();
constructor(private community: CreateCommunityComponent ) { }
selectcore(core: any) {
// alert(core.value);
this.notify.emit(core.value);
}
}

As you are passing Values through EventEmitter you need to pass it with Child component Declaration in parent.html.
<browser (notify)="onSelect($event)" [template]="coreActivity" (onselect)="onselect($event)" ></browser>
And in function under parent.ts :
onSelect(select)
{
console.log(select.value);
}

In order to access child.html in parent, you need to create a another component and use that component name as tag in parent.
like component name is "child"
keep <child></child> in parent

Related

How to Pass data between two sibling components?

<container>
<navbar>
<summary></summary>
<child-summary><child-summary>
</navbar>
<content></content>
</container>
So, in I have a select that do send value to and .
OnSelect method is well fired with (change) inside component.
So, I tried with #Input, #Output and #EventEmitter directives, but I don't see how retrieve the event as #Input of the component, unless to go on parent/child pattern. All examples I've founded has a relation between component.
EDIT : Example with BehaviorSubject not working (all connected service to API works well, only observable is fired at start but not when select has value changed)
Start by creating a BehaviorSubject in the service
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
I create a sample to send message between component by using BehaviorSubject
#Injectable()
export class MyService {
private messageSource = new BehaviorSubject<string>('service');
currentMessage = this.messageSource.asObservable();
constructor() {
}
changeMessage(message: string) {
this.messageSource.next(message)
}
}
You can refer at https://stackblitz.com/edit/behavior-subject-2019

How to get access to TemplateRef from Outside a Component

From the doc, I can read:
Alternatively you can query for the TemplateRef from a Component or a Directive via Query.
Question:
How to Query a TemplateRef from an external Component?
Here some code from the doc:
#Component(
selector: 'child-cmp',
template: '<p>child</p>',
)
class ChildCmp {
void doSomething() {}
}
#Component(
selector: 'some-cmp',
template: '''
<child-cmp #child1></child-cmp>
<child-cmp #child2></child-cmp>
<child-cmp #child3></child-cmp>
''',
directives: [ChildCmp],
)
class SomeCmp implements AfterViewInit {
#ViewChildren('child1, child2, child3')
List<ChildCmp> children;
#override
void ngAfterViewInit() {
// Initial children are set
for (var child in children) {
child.doSomething();
}
}
}
How from SomeCmp can I get ChildCmp's TemplateRef. What should I do?
I'm not sure how to access a component's template ref but if you figure that out then you can simply make that a public property in the child component and access it from the parent.
A workaround that I did is simply use an <ng-template> to store the child's template and render it in an <ng-container>. I them store the reference to that ng-template in the child component as a public property and it is easily accessible from the parent. I don't believe this is the cleanest solution but it satisfies your question.
You can check out my solution at this stackblitz: https://stackblitz.com/edit/angular-ivy-kahwbz?file=src/app/app.component.ts

Angular 2 output from router-outlet

I want to make navigation from child components that render inside router-outlet.
My parent component have a router config and I want to navigate manually on some event. But I don't know how I can pass from child to parent some data (for navigation) without output. Because this construction is non working
<router-outlet (navigateTo)="navigateToMessagePart($event)"></router-outlet>
How I can do it in right way? Maybe navigate it from child? But how I can get parent methods from child.
Many thanks for any help!
<router-outlet></router-outlet> can't be used to emit an event from the child component. One way to communicate between two components is to use a common service.
Create a service
shared-service.ts
import { Observable } from "rxjs/Observable";
import { Injectable } from "#angular/core";
import { Subject } from "rxjs/Subject";
#Injectable()
export class SharedService {
// Observable string sources
private emitChangeSource = new Subject<any>();
// Observable string streams
changeEmitted$ = this.emitChangeSource.asObservable();
// Service message commands
emitChange(change: any) {
this.emitChangeSource.next(change);
}
}
Now inject the instance of the above service in the constructor of both the parent and child component.
The child component will be emitting a change every time the onClick() method is called
child.component.ts
import { Component } from "#angular/core";
#Component({
templateUrl: "child.html",
styleUrls: ["child.scss"]
})
export class ChildComponent {
constructor(private _sharedService: SharedService) {}
onClick() {
this._sharedService.emitChange("Data from child");
}
}
The parent component shall receive that change. To do so,capture the subscription inside the parent's constructor.
parent.component.ts
import { Component } from "#angular/core";
#Component({
templateUrl: "parent.html",
styleUrls: ["parent.scss"]
})
export class ParentComponent {
constructor(private _sharedService: SharedService) {
_sharedService.changeEmitted$.subscribe(text => {
console.log(text);
});
}
}
<router-outlet></router-outlet> is just a placeholder for adding routed components. There is no support for any kind of binding.
You can create a custom <router-outlet> that allows you to do that or more common, use a shared service to communicate between parent component and routed component.
For more details see https://angular.io/docs/ts/latest/cookbook/component-communication.html
update
There is now an event that allows to get the added component
<router-outlet (activate)="componentAdded($event)" (deactivate)="componentRemoved($event)"></router-outlet>
which allows to communicate (call getters, setters, and methods) with the component in componentAdded()
A shared service is the preferred way though.
The answer given above is correct and complete. I just want to add for those who the solution didn't work for them that they should add the service to providers only in the parent component and not the child to ensure that you get a singleton of the service, otherwise two service instances will be created.
This response is inspired by the comment of #HeisenBerg in the previous response.
I changed a little from Antara Datta's answer.
I created a Subscriber service
import {Injectable} from '#angular/core';
import {Subject} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class Subscriber<T>
{
protected observable = new Subject<T>();
public next(item: T)
{
this.observable.next(item);
}
public subscribe(callback: (item:T)=>void) {
this.observable.subscribe(callback);
}
}
Whenever I need two components to share some information, I inject this service in the constructor which subscribe to it:
constructor(protected layoutOptions: Subscriber<Partial<LayoutOptions>>)
{
layoutOptions.subscribe(options => this.options = Object.assign({}, this.options, options));
}
and the one which updates it
constructor(protected router: Router, protected apiService: ApiService, protected layoutOptions: Subscriber<Partial<LayoutOptions>>)
{
this.layoutOptions.next({showNavBar: false});
}
It escapes my understanding why the router does not forward the "#Outputs".
I ended up dispatching barebones DOM events
// dom node needs to be a reference to a DOM node in your component instance
// preferably the root
dom.dispatchEvent(
new CustomEvent('event', {
detail: payload, // <- your payload here
bubbles: true,
composed: true,
})
);
You can catch it anywhere up the DOM tree like any other DOM event
Note: you need to unpack the payload from { detail: payload } on the receiving end..

Angular 2 : How to watch service variables? [duplicate]

I am trying to implement something like a delegation pattern in Angular.
When the user clicks on a nav-item, I would like to call a function which then emits an event which should in turn be handled by some other component listening for the event.
Here is the scenario: I have a Navigation component:
import {Component, Output, EventEmitter} from 'angular2/core';
#Component({
// other properties left out for brevity
events : ['navchange'],
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
#Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
Here is the observing component:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
The key question is, how do I make the observing component observe the event in question ?
Update 2016-06-27: instead of using Observables, use either
a BehaviorSubject, as recommended by #Abdulrahman in a comment, or
a ReplaySubject, as recommended by #Jason Goemaat in a comment
A Subject is both an Observable (so we can subscribe() to it) and an Observer (so we can call next() on it to emit a new value). We exploit this feature. A Subject allows values to be multicast to many Observers. We don't exploit this feature (we only have one Observer).
BehaviorSubject is a variant of Subject. It has the notion of "the current value". We exploit this: whenever we create an ObservingComponent, it gets the current navigation item value from the BehaviorSubject automatically.
The code below and the plunker use BehaviorSubject.
ReplaySubject is another variant of Subject. If you want to wait until a value is actually produced, use ReplaySubject(1). Whereas a BehaviorSubject requires an initial value (which will be provided immediately), ReplaySubject does not. ReplaySubject will always provide the most recent value, but since it does not have a required initial value, the service can do some async operation before returning it's first value. It will still fire immediately on subsequent calls with the most recent value. If you just want one value, use first() on the subscription. You do not have to unsubscribe if you use first().
import {Injectable} from '#angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class NavService {
// Observable navItem source
private _navItemSource = new BehaviorSubject<number>(0);
// Observable navItem stream
navItem$ = this._navItemSource.asObservable();
// service command
changeNav(number) {
this._navItemSource.next(number);
}
}
import {Component} from '#angular/core';
import {NavService} from './nav.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
subscription:Subscription;
constructor(private _navService:NavService) {}
ngOnInit() {
this.subscription = this._navService.navItem$
.subscribe(item => this.item = item)
}
ngOnDestroy() {
// prevent memory leak when component is destroyed
this.subscription.unsubscribe();
}
}
#Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
<div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>`
})
export class Navigation {
item = 1;
constructor(private _navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this._navService.changeNav(item);
}
}
Plunker
Original answer that uses an Observable: (it requires more code and logic than using a BehaviorSubject, so I don't recommend it, but it may be instructive)
So, here's an implementation that uses an Observable instead of an EventEmitter. Unlike my EventEmitter implementation, this implementation also stores the currently selected navItem in the service, so that when an observing component is created, it can retrieve the current value via API call navItem(), and then be notified of changes via the navChange$ Observable.
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import {Observer} from 'rxjs/Observer';
export class NavService {
private _navItem = 0;
navChange$: Observable<number>;
private _observer: Observer;
constructor() {
this.navChange$ = new Observable(observer =>
this._observer = observer).share();
// share() allows multiple subscribers
}
changeNav(number) {
this._navItem = number;
this._observer.next(number);
}
navItem() {
return this._navItem;
}
}
#Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
subscription: any;
constructor(private _navService:NavService) {}
ngOnInit() {
this.item = this._navService.navItem();
this.subscription = this._navService.navChange$.subscribe(
item => this.selectedNavItem(item));
}
selectedNavItem(item: number) {
this.item = item;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
#Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
<div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
`,
})
export class Navigation {
item:number;
constructor(private _navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this._navService.changeNav(item);
}
}
Plunker
See also the Component Interaction Cookbook example, which uses a Subject in addition to observables. Although the example is "parent and children communication," the same technique is applicable for unrelated components.
Breaking news: I've added another answer that uses an Observable rather than an EventEmitter. I recommend that answer over this one. And actually, using an EventEmitter in a service is bad practice.
Original answer: (don't do this)
Put the EventEmitter into a service, which allows the ObservingComponent to directly subscribe (and unsubscribe) to the event:
import {EventEmitter} from 'angular2/core';
export class NavService {
navchange: EventEmitter<number> = new EventEmitter();
constructor() {}
emit(number) {
this.navchange.emit(number);
}
subscribe(component, callback) {
// set 'this' to component when callback is called
return this.navchange.subscribe(data => call.callback(component, data));
}
}
#Component({
selector: 'obs-comp',
template: 'obs component, index: {{index}}'
})
export class ObservingComponent {
item: number;
subscription: any;
constructor(private navService:NavService) {
this.subscription = this.navService.subscribe(this, this.selectedNavItem);
}
selectedNavItem(item: number) {
console.log('item index changed!', item);
this.item = item;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
#Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">item 1 (click me)</div>
`,
})
export class Navigation {
constructor(private navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navService.emit(item);
}
}
If you try the Plunker, there are a few things I don't like about this approach:
ObservingComponent needs to unsubscribe when it is destroyed
we have to pass the component to subscribe() so that the proper this is set when the callback is called
Update: An alternative that solves the 2nd bullet is to have the ObservingComponent directly subscribe to the navchange EventEmitter property:
constructor(private navService:NavService) {
this.subscription = this.navService.navchange.subscribe(data =>
this.selectedNavItem(data));
}
If we subscribe directly, then we wouldn't need the subscribe() method on the NavService.
To make the NavService slightly more encapsulated, you could add a getNavChangeEmitter() method and use that:
getNavChangeEmitter() { return this.navchange; } // in NavService
constructor(private navService:NavService) { // in ObservingComponent
this.subscription = this.navService.getNavChangeEmitter().subscribe(data =>
this.selectedNavItem(data));
}
You can use either:
Behaviour Subject:
BehaviorSubject is a type of subject, a subject is a special type of observable which can act as observable and observer
you can subscribe to messages like any other observable and upon subscription, it returns the last value of the subject
emitted by the source observable:
Advantage: No Relationship such as parent-child relationship required to pass data between components.
NAV SERVICE
import {Injectable} from '#angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class NavService {
private navSubject$ = new BehaviorSubject<number>(0);
constructor() { }
// Event New Item Clicked
navItemClicked(navItem: number) {
this.navSubject$.next(number);
}
// Allowing Observer component to subscribe emitted data only
getNavItemClicked$() {
return this.navSubject$.asObservable();
}
}
NAVIGATION COMPONENT
#Component({
selector: 'navbar-list',
template:`
<ul>
<li><a (click)="navItemClicked(1)">Item-1 Clicked</a></li>
<li><a (click)="navItemClicked(2)">Item-2 Clicked</a></li>
<li><a (click)="navItemClicked(3)">Item-3 Clicked</a></li>
<li><a (click)="navItemClicked(4)">Item-4 Clicked</a></li>
</ul>
})
export class Navigation {
constructor(private navService:NavService) {}
navItemClicked(item: number) {
this.navService.navItemClicked(item);
}
}
OBSERVING COMPONENT
#Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
itemClickedSubcription:any
constructor(private navService:NavService) {}
ngOnInit() {
this.itemClickedSubcription = this.navService
.getNavItemClicked$
.subscribe(
item => this.selectedNavItem(item)
);
}
selectedNavItem(item: number) {
this.item = item;
}
ngOnDestroy() {
this.itemClickedSubcription.unsubscribe();
}
}
Second Approach is Event Delegation in upward direction child -> parent
Using #Input and #Output decorators parent passing data to child component and child notifying parent component
e.g Answered given by #Ashish Sharma.
If one wants to follow a more Reactive oriented style of programming, then definitely the concept of "Everything is a stream" comes into picture and hence, use Observables to deal with these streams as often as possible.
you can use BehaviourSubject as described above or there is one more way:
you can handle EventEmitter like this:
first add a selector
import {Component, Output, EventEmitter} from 'angular2/core';
#Component({
// other properties left out for brevity
selector: 'app-nav-component', //declaring selector
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
#Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
Now you can handle this event like
let us suppose observer.component.html is the view of Observer component
<app-nav-component (navchange)="recieveIdFromNav($event)"></app-nav-component>
then in the ObservingComponent.ts
export class ObservingComponent {
//method to recieve the value from nav component
public recieveIdFromNav(id: number) {
console.log('here is the id sent from nav component ', id);
}
}
You need to use the Navigation component in the template of ObservingComponent ( dont't forget to add a selector to Navigation component .. navigation-component for ex )
<navigation-component (navchange)='onNavGhange($event)'></navigation-component>
And implement onNavGhange() in ObservingComponent
onNavGhange(event) {
console.log(event);
}
Last thing .. you don't need the events attribute in #Componennt
events : ['navchange'],
I found out another solution for this case without using Reactivex neither services. I actually love the rxjx API however I think it goes best when resolving an async and/or complex function. Using It in that way, Its pretty exceeded to me.
What I think you are looking for is for a broadcast. Just that. And I found out this solution:
<app>
<app-nav (selectedTab)="onSelectedTab($event)"></app-nav>
// This component bellow wants to know when a tab is selected
// broadcast here is a property of app component
<app-interested [broadcast]="broadcast"></app-interested>
</app>
#Component class App {
broadcast: EventEmitter<tab>;
constructor() {
this.broadcast = new EventEmitter<tab>();
}
onSelectedTab(tab) {
this.broadcast.emit(tab)
}
}
#Component class AppInterestedComponent implements OnInit {
broadcast: EventEmitter<Tab>();
doSomethingWhenTab(tab){
...
}
ngOnInit() {
this.broadcast.subscribe((tab) => this.doSomethingWhenTab(tab))
}
}
This is a full working example:
https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE

is it possible to access attributes set in HTML in an angular.dart constructor?

I cannĀ“t access the attribute val in the constructor of this component
#NgComponent(
selector: 'dartcomp',
templateUrl: 'dartComp.html',
publishAs: 'ctrl',
map: const
{
'val' : '#val'
}
)
class DartComp
{ String val;
DartComp()
{ print("construktor DartComp $val");
}
}
which was used in index.html
<dartcomp id="dc" val="x"></dartcomp>
Is there a way to access val in the constructor?
Extend NgAttachAware, implement the method attach() and access your field there. When attach() is called your val is already properly initialized.

Resources