How to get access to TemplateRef from Outside a Component - dart

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

Related

angular2 need to access the child value in parent class

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

is there a way lazy load a component in angular 2 dart?

I have a component that uses another component with a ngIf statement. I would like to only load the second component once the ngIf evaluates to true.
EDIT: found an article that can almost do what I need:
https://medium.com/#matanlurey/lazy-loading-with-angular-dart-14f58004f988. However, after the library loaded, it takes the whole view of the component. In my case, I need to insert it into a specific place in the html of the parent component.
Something like:
import '../other/edit_component.dart' deferred as otherEdit;
#Component(
selector: 'edit-component',
template: '<other-component *ngIf="canOther"></other-component>
<button type="button" (click)="go()"></button>',
directives: const [
otherEdit.OtherComponent
]
)
class EditComponent {
#Input()
bool canOther = false;
go() {
otherEdit.loadLibrary();
canOther = true;
}
}
I do not think you can do it directly. What you can do instead is using DynamicComponent from Angular2_components and pass the type after lazily loading it.
Just made it work. Used the DynamicComponent as example from rkj answer.
// the lib that has the component to be loaded
import 'package:somecomponent.dart' deferred as mycomponent;
class mainComponent {
// <div #holder></div> element that we will append the component
#ViewChild('holder', read: ViewContainerRef)
ViewContainerRef holder;
// this will load the component dynamically
final DynamicComponentLoader _componentLoader;
load() {
// clear the html
holder.clear();
// load the component dynamically
ComponentRef componentRef = await _componentLoader
.loadNextToLocation(componentType, holder);
// set some attributes like you would with [attributes]="somevar"
componentRef.instance
..attribute = somevar;
}
mainComponent(this. _componentLoader){}
}

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..

Angular2: Accessing child nodes from a template

I have a component and I would like accessing some child nodes from the template. I achieved to access the details div, but I don't know why the code works. What exactly does the Future class? And why the first line prints null? Is this the correct way to access child nodes from the template?
#Component(selector: 'hero-detail', template: '<div #details></div>')
class HeroDetailComponent implements OnInit {
Hero hero;
#ViewChild('details')
var details;
Future ngOnInit() async {
// why this command prints null?
print(details);
// why this command prints "Instance of 'ElementRef_'"
new Future(() => print(details));
}
}
#Component(selector: 'hero-detail', template: '<div #details></div>')
class HeroDetailComponent implements OnInit {
Hero hero;
// Angular generates additional code that looks up the element
// from the template that has a template variable `#details
// and assigns it to `var details`
#ViewChild('details')
var details;
// I don't think Future does anything here.
Future ngOnInit() async {
// why this command prints null?
// this is too early. `#ViewChild()` is only set in `ngAfterViewInit`
// at this point the view is not yet fully created and therefore
// `#details can't have been looked up yet
print(details);
// why this command prints "Instance of 'ElementRef_'"
// this delays `print(details)` until the next Dart event loop
// and `details` is then already lookup up and assigned
new Future(() => print(details));
}
// this is the right place
// needs `class HeroDetailComponent implements OnInit, AfterViewInit {
ngAfterViewInit() {
print(details);
}
}

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