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

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));
}
}

Related

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()

Confusing while Passing data between the components

I am new in angular 6, I am creating the project using angular 6. I am coming to the problem while sharing the data.
Here is my code:
1) Component Sidebar:
selectedCategory(type:any) {
this.loginService.categoryType = type; // need to pass this data
}
2) List Comp:
export class ListPostsComponent implements OnInit {
ngOnInit() {
// here I need the data
}
}
3) Service:
export class LoginService {
categoryType:any;
}
In your service make categoryType a Subject and call the next() when you need to pass data to another component:
#Injectable({
providedIn: 'root',
})
export class LoginService {
private categoryType: Subject<any> = new Subject<any>();
public categoryType$ = this.categoryType.asObservable();
public sendData(data: any){
this.categoryType.next(data);
}
}
Now in your Component Sidebar, you need to inject the service LoginService and call the sendData method:
constructor(private loginService: LoginService ){ }
selectedCategory(type:any) {
this.loginService.sendData(type);
}
Since a Subject is both an Observer and an Observable you can subscribe to the Subject and listen for changes in the component you wish to receive the data:
export class ListPostsComponent implements OnInit {
constructor(private loginService: LoginService ){ }
ngOnInit() {
this.loginService.categoryType$.subscribe((data) => {
//use your data here
});
}
}
Here is a working example of the above solution in Stackblitz: https://stackblitz.com/edit/angular-2sld4k?file=src%2Fapp%2Floginservice.service.ts

Access Superclass Fields from Angular 5 Component

I have a superclass which contains common functionality for components.
export class AbstractComponent implements OnInit {
public user: User;
constructor(public http: HttpClient) {
}
ngOnInit(): void {
this.http.get<User>('url').subscribe(user => {
this.user = user;
});
}
}
I have a subclass which implements this superclass.
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent extends AbstractComponent {
constructor(public http: HttpClient) {
super(http);
}
}
In the headers template I am trying to access the user
<mat-toolbar color="primary">
<span *ngIf="user">Welcome {{user.username}}!</span>
</mat-toolbar>
But the user field is not being resolved. How can I access a superclass's fields from a subclass?
You are getting an error because the user object is not available at load.
Either initalise it or use the safe navigation operator (?.) inside your template
initalise:
public user: User = new User();
safe navigation:
<span>Welcome {{user?.username}}!</span>
This approach works but it is not a good practice. In such cases it would be better to use async pipe:
export class AbstractComponent {
user$;
constructor() {
// your real http request should be here
this.user$ = Observable.of({name: 'John Doe'});
}
}
#Component({
selector: 'my-app',
template: `
<div>Hello {{(user$ | async).name}}</div>
`,
})
export class App extends AbstractComponent {
constructor() {
super();
}
}

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.

Angular2 pass function argument to constructor

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) {
}
}

Categories

Resources