Access Material Table DataSource from component.ts - angular-material

I'm dealing with a custom wrapper (not mine and I can't refactor) around a Material table. I've been asked to implement a feature that requires accessing the paginator, sort, and filter. Is this possible, or am I stuck with my implementation using a mat-table without the custom wrapper? If it matters: I'm trying to update the dataSource records that are displayed on the paginator's currently selected page.
Partial code:
#Component({ selector: 'custom-table' })
export class CustomTable {
#Input customDatasource: any[];
// dataSource for mat-table
ngOnInit() {
this.dataSource = new MatTableDataSource<any>(this.customDatasource);
And in the calling view component:
<custom-table [customDatasource]="mydataset"></custom-table>
And what I'd like to do from the calling component:
onSomeButtonClick() {
const foo = dataSourceOfCustomTableElement; // This is what I'm struggling with
const page = foo.paginator.page;
const filtered = foo.filterData(mydataset);
// etc...

Related

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

NG2 - Input from original component is not updating after original value changes

I am trying to use NG2 binding but for some reason it doesnt work when i have multiple nested #Input data
I have read and tried solutions from:
Angular 2 - View not updating after model changes
but without luck.
What I basically have is 3 components (parent -> child -> grandson)
the parent has a data field, the child and the grandson take it as input.
the original value passes without a problem
but when i try to update this value in the parent component, the child and grandson arent updated.
it is in the spirit of the following simple components:
#Component({
selector: 'parent',
template: '<button (click)="updateData()">
<child [data]="data"></child>'
})
export class ChildComponent { var data = { a : 0};
click() { this.data.a+=1; }
}
#Component({
selector: 'child',
template: '<grand [data]="data"></grand>' })
export class GrandComponent {
#Input data:any;
}
#Component({
selector: 'grand',
template: '<div>{{data.a}}</div>' })
export class ParentComponent {
#Input data:any;
}

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

Resources