Angular2 pass function argument to constructor - javascript

I'm trying to make some kind of #Component factory where I call a function that returns a component. And I want to pass the widgetName to the constructor or to the super constructor. How do I pass an argument to a constructor?
export function createCommonKendoComponent(selector: string, **widgetName**: string) {
#Component({
selector: selector,
inputs: ['bound', 'role'],
bindings: [ElementRef]
})
#View({ template: '<ng-content></ng-content>' })
class CommonComponent extends KendoComponent {
constructor(elementRef) {
super(elementRef, **widgeteName**);
}
}
return CommonComponent;
}

You add it as provider somewhere. Because a function doesn't have a type that can be used as provider you need to use a token. A token can be either a string or an OpaqueToken
var token = new OpaqueToken('myfunction');
bootstrap(AppComponent, [
provide(token,
{useValue: (selector: string, **widgetName**: string) => {
createCommonKendoComponent(selector, **widgetName**}})]);
class CommonComponent extends KendoComponent {
constructor(elementRef:ElementRef, #Inject(token) private compFactory) {
}
}

Related

Multilevel data passing in Angular

Suppose you have a parent component A and inside of it you have some variable x. You would like to pass this variable to the child component B. Easy! Just use #Input annotation and call it a day. But what if B has another child component C? How would we pass x from A to C? I tried using the same approach to pass it from B to C, but it only passes the value undefined.
You can use a common service file which is data.service.ts file in this case. This service will be injected by both the parent and grand child. When component A which is grand parent here want to send a data it will call the deliverMsg method of the data service file. The component C which is grand child will listen to this change by injecting the same data.service
data.service.ts
// relevant imports
#Injectable()
export class DataService {
private message = new BehaviorSubject('default message');
portMessage = this.message.asObservable();
constructor() { }
deliverMsg(message: string) {
this.message.next(message)
}
}
parent.component.ts
//all relevant imports
#Component({
selector: 'app-parent-a',
template: 'html file url',
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
}
newMessage() {
this.data.deliverMsg("Hello from Grand Parent")
}
}
grandchild.component.ts
// all relevant imports
#Component({
selector: 'app-sibling',
template: 'template',
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.portMessage.subscribe(message => this.message = message)
}
}
Alternatively you can also you NgRx

How do I call a function in a class from another class in Angular 6?

Here's my code:
import { Component, OnInit, ViewChild } from '#angular/core';
import { AuthService } from '../core/auth.service';
import { MatRadioButton, MatPaginator, MatSort, MatTableDataSource } from '#angular/material';
import { SelectionModel } from '#angular/cdk/collections';
import { OrdersService } from '../orders.service';
export interface DataTableItem {
ordersn: string;
order_status: string;
update_time: number;
}
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
radioValue: number;
dataSource = new UserDataSource(this.orderService);
selection = new SelectionModel<any>(true, []);
// Sorting and pagination
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
// Columns displayed in the table. Columns IDs can be added, removed, or reordered.
displayedColumns = ['ordersn', 'order_status', 'update_time'];
// Filter
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
// Whether the number of selected elements matches the total number of rows.
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
// Selects all rows if they are not all selected; otherwise clear selection.
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
constructor(public auth: AuthService, private orderService: OrdersService) {
}
onSelectionChange(radioSelection: MatRadioButton) {
this.radioValue = radioSelection.value;
console.log(this.radioValue);
}
ngOnInit() {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
}
export class UserDataSource extends MatTableDataSource<any> {
constructor(private orderService: OrdersService) {
super();
this.orderService.GetOrdersList().subscribe(d => {
this.data = d.orders;
});
}
radioFilter() {
const array = [];
this.orderService.GetOrdersList().subscribe(d => {
for (const entry of d.orders) {
if (entry.order_status === 'READY_TO_SHIP') {
array.push(entry);
}
}
this.data = array;
console.log(array);
});
}
}
I'm trying to call radioFilter() from HomeComponent. What I've tried:
Implementing #ViewChild in HomeComponent but I would get this error: Class 'UserDataSource' used before its declaration.
Importing UserDataSource and then added to the constructor in HomeComponent. I would get this error: Getting Uncaught Error: Can't resolve all parameters for HomeComponent
I'm kind of out of anymore idea, thus any suggestion is much appreciated. Thanks!
Getting Uncaught Error: Can't resolve all parameters for HomeComponent
First of all your dataSource is not registered in a ngModule as injectable.
So it's not possible to inject it to the constructor in HomeComponent.
I also don't think you want to do that because ngMaterial-dataSources are stateful and injectables shouldn't be stateful.
Class 'UserDataSource' used before its declaration
Your dataSource is not a ViewChild in your component's template. It's just an object (without a html-template). The error you get is that annotations in typeScript are processed on compile/transpile time. But the UserDataSource class is declared below the HomeComponent. You are using it before it's declared. You could just put it above the HomeComponent but better put it in a new file and import it. But that's not the solution.
Possible solution
I don't get why you cannot just call the radioFilter method.
It's a public method of your UserDataSource and there is an instantiated object in HomeComponent called dataSource. Just make sure to not call it in the constructor. Member variables are processed after the constructor is called. But imho you can just call dataSource.radioFilter()

Angular How to bind service instance and keep it in different classes

How can i pass a service method in a super class from a sub class?
I have next service
import { Injectable, Injector } from '#angular/core';
import { HttpParams } from '#angular/common/http';
import 'rxjs/add/operator/map';
#Injectable
export class MyService {
constructor(private http: Httpclient) {}
getData(param1: string, param2: string): Observable<Model>{
let params = new HttpParams();
{...}
return this.http.get<Model>(this.baseUrl, { params })
.map((data) => new Model());
}
}
Class A as super class
export ClassA implements OnChanges {
constructor(private _serviceMethod: (param1: string, param2: string) => Observable<Model>){}
ngOnChanges() {
this.getData(somedata, somedata);
}
getData(param1?: string, param2?: string) {
this._serviceMethod(param1, param2)
.subscribe((response: Model) => {
this.data = response;
}, (error) => {
{...}
});
}
}
and ClassBComponent as sub class
#Component({
selector: 'class-b',
templateUrl: 'class-b.component.html',
styleUrls: ['class-b.component.css'],
})
export ClassBComponent extends ClassA {
constructor(private myService: MyService){
super(myService.getData);
}
}
ClassBComponent is one of many components that extends ClassA and share some logic in getData(somedata, somedata) method of ClassA. Now, if i do that
this.http.get in MyService throw "TypeError: Cannot read property 'get' of undefined". My http of type HttClient is undefined even if is injected in MyService constructor because 'this' in MyService does not get reference of service anymore and points at ClassBComponent. How can i bind the instance of MyService and used it when i pass the methods in super() of sub classes? Do you have any idea how can i do that? Thank you :)
If a function is supposed to be used as a callback (event listener, etc.), it preferably should be bound on class construction, e.g. with bind or arrow function.
Since getData isn't supposed to be a callback but is used like one, it should be bound in-place:
export ClassBComponent extends ClassA {
constructor(private myService: MyService){
super(myService.getData.bind(myService));
}
}

typescript in angular2, how to access class variables through this

Given this code, how can I access to the object "sessions"? it fails due to "this" being null:
/// <reference path="chrome/chrome-app.d.ts" />
import { Component, Input, OnInit } from '#angular/core';
import { AppService } from './app.service';
#Component({
selector: 'tabs',
templateUrl: './templates/app.html',
providers: [ AppService ]
})
export class AppComponent implements OnInit {
public sessions : Object;
constructor( private appService : AppService ) {}
getBookmarkLists() {
console.log(this.sessions) // it gives undefined
this.sessions['test'] = 'yea'; // it fails
this.appService.getBookmarks().then(function(bookmarks : any) {
console.log(this.sessions) // it fails
});
}
ngOnInit() {
this.getBookmarkLists();
}
}
What I would expect is to be able to access to the variable and populate it.
You didn't initialized this Sessions object anywhere, should be as far I know:
export class AppComponent implements OnInit {
public sessions: Session[] = []; // You forgot here to initialize it
constructor( private appService : AppService ) {}
getBookmarkLists() {
console.log(this.sessions) // no it shouldn't give undefined
this.sessions['test'] = 'yea'; // and this shouldn't fail
this.appService.getBookmarks().then((bookmarks : any) => {
// this should be an arrow function or function bound to use it
// otherwise this will point to the function itself.
console.log(this.sessions) // it shouldn't fail
});
}
ngOnInit() {
this.getBookmarkLists();
}
}
with the sessions = []; being the crucial part.
So it's not only an issue of this which references the class instance in methods as it should.
The callback passed to the then should be an arrow function not a classic function, to keep the this reference to the class instance.

Angular 2. Pass parameter to a component

I would like to pass a string parameter to my component. Depending on passing parameter i will pass different parameters for services in my component. I do next: In index.html call my component, passing parameter.
<top [mode]="tree">Loading...</top>
In my component i include Input from angular2/core
import {Input, Component, OnInit} from 'angular2/core';
In my component`s class i declare an input
#Input() mode: string;
And with console.log() i try to catch my passing parameter of 'tree', but it`s undefined.
console.log(this, this.mode);
The full code of a component file:
import {Http, HTTP_PROVIDERS} from 'angular2/http';
import {Input, Component, OnInit} from 'angular2/core';
import {ParticipantService} from '../services/participant.service';
import {orderBy} from '../pipes/orderby.pipe';
#Component({
selector: 'top',
templateUrl: 'dev/templates/top.html',
pipes: [orderBy],
providers: [HTTP_PROVIDERS, ParticipantService]
})
export class AppTopComponent implements OnInit {
constructor (private _participantService: ParticipantService) {}
errorMessage: string;
participants: any[];
#Input() mode: string;
ngOnInit() {
console.log(this, this.mode);
this.getParticipants('top3');
var self = this;
setInterval(function() {
self.getParticipants('top3');
}, 3000);
}
getParticipants(public mode: string) {
this._participantService.getParticipants(mode)
.then(
participants => this.participants = participants,
error => this.errorMessage = <any>error
);
}
}
When you use [...], the value you provide corresponds to an expression that can be evaluated.
So tree must be something that exists in the parent component and correspond to a string.
If you want to use the string tree, use this:
<top mode="tree">Loading...</top>
You can notice that such parameters can't be used for root component. See this question for more details:
Angular 2 input parameters on root directive
As a workaround for the limitation Thierry explained you can use
constructor(private _participantService: ParticipantService,
elRef:ElementRef) {
this.mode=elRef.nativeElement.getAttribute('mode');
}
you need to wait until template is bound to DOM. in order to do this, you have to implement AfterViewInit
export class AppTopComponent implements AfterViewInit{
public ngAfterViewInit() {
console.log(this, this.mode);
this.getParticipants('top3');
var self = this;
setInterval(function() {
self.getParticipants('top3');
}, 3000);
}
}

Categories

Resources