So I asked a question a few days ago and got some headway on a solution, however now I'm stuck at another wall I'm unsure how to get over.
I have two parent components, a shared view/component with an extended base component, and a service all hooked together. The objective is to use the two parent components to drive what data is shown within the shared component. The two parent components use references to service methods passed into the shared component to get the data.
I've reached an issue where my http.get is always undefined no matter what I try. I've instantiated it like I do in my other services but I've had no luck. I suspect this is caused by how i pass in my service references. Code below:
Parent Component Code:
// PARENT COMPONENT
myData$: Observable<myType>;
searchMethod: Function;
constructor(private myService){
this.myData$ = this.myService.myData$;
this.searchMethod = this.myService.searchData;
}
// PARENT COMPONENT HTML
<app-shared-component
[myData$] = "myData$"
[searchMethod]="searchMethod">
</app-shared-component>
Shared Component Code:
export class MySharedComponent extends BaseComponent<MyType> implements OnInit {
#Input() myData$: Observable<myType>;
#Input() searchMethod: Function;
constructor() { super(); }
ngOnInit(): void {
this.data$ = this.myData$;
}
search(): void {
this.searchMethod().subscribe(//do something);
}
Base Component Code:
#Input data$: Observable<T>;
ngOnInit(): void {
this.data$.subscribe((response: T) => //do something);
super.ngOnInit();
}
Service Code:
private myDataSubject = new BehaviorSubject<MyType>(new MyType());
get myData$(): Observable<MyType> {
return this.myDataSubject.asObservable();
}
constructor(private http: HttpClient) { }
searchData(): Observable<void> {
return new Observable<void>(observer => {
this.http.get<MyType>(
'http://myuri'
).subscribe(
response => {
// do something
},
() => observer.error(),
() => observer.complete()
);
});
}
It looks like you're losing the context of your service when you set this.searchMethod = this.myService.searchData in your parent component. It should work if you change searchData() { to an arrow function: searchData = (): Observable<void> => {.
Related
I'm trying to find a way to share a variable between my components in Angular. My variable that I pass to my child components are interchangeable between their child components. I used service for this, but still could not solve the problem. I am sharing a demo by simplifying my operations using BehaviorSubject.
demo : https://stackblitz.com/edit/angular-ivy-6usdww?file=src%2Fapp%2Fhello.component.ts
My problem is:
i am sending variables from my app.component.ts component to hello and hello-a components. i am mutating variable in hello component. However, just before that I point the first version to a service. however, the changed status is forwarded.
hello.component
export class HelloComponent {
#Input() colDef: any[];
col: any[];
constructor(private columnDefService: ColumnDefService) {}
ngOnInit() {
let colDefFirst = Object.assign([], this.colDef);
this.columnDefService.setFirstColDef(colDefFirst);
this.setColumnDef(this.colDef);
}
setColumnDef(colDef: any[]): void {
let rC: any = Object.assign([], colDef);
rC[0].hide = true;
this.columnDefService.setColDef([].concat(rC));
this.columnDefService.colDef$.subscribe((colDef: any) => {
this.col = colDef;
});
}
}
hello-a.component
export class HelloAComponent {
#Input() colDef: any[];
firstColDef: any[] = [];
constructor(private ColumnDefService: ColumnDefService) {}
ngOnInit() {
this.ColumnDefService.firstColDef$.subscribe((firstColDef: any) => {
console.log('firstColDef', firstColDef);
this.firstColDef = firstColDef;
});
}
}
coldef.service
#Injectable({
providedIn: 'root'
})
export class ColumnDefService {
private colDefSource = new BehaviorSubject([]);
private firstColDefSource = new BehaviorSubject([]);
colDef$ = this.colDefSource.asObservable();
firstColDef$ = this.firstColDefSource.asObservable();
setColDef(colDef: any) {
this.colDefSource.next(colDef);
}
setFirstColDef(colDef: any) {
this.firstColDefSource.next(colDef);
}
}
this is what i want to do:
storing my variable in a component for future use. i want to continuously change my variable on demand in my other component.
thanks for your advice
I, am using data service to share the data between the component. However, this seems not working for me.
Got the reference from here
Angular to update UI from the child component reflect the value to the parent component
https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/
I tried the same logic as above but seems to not work for me.
Here is the html binding for the angular material
<mat-progress-bar mode="indeterminate" *ngIf="commonViewModel.showProgressBar()"></mat-progress-bar>
Parent component
export class AppComponent {
constructor(public commonViewModel: CommonViewModel) { }
ngOnInit() {
this.isLoding();
}
isLoding() {
console.log("app=" + this.commonViewModel.showProgressBar());
return this.commonViewModel.showProgressBar();
}
}
Child Component
export class HomeComponent {
private GetHomeItemUrl: string = "Home/GetHomeItem";
private _homeItemService: GenericHttpClientService;
constructor(public commonViewModel: CommonViewModel) {
this.getHomeItemHttpCall();
}
private getHomeItemHttpCall(): void {
this.commonViewModel.setProgressBarShow = true;
this._homeItemService.GenericHttpGet<GenericResponseObject<HomeViewModel>>(this.GetHomeItemUrl).subscribe(data => {
if (data.isSuccess) {
this.commonViewModel.setProgressBarShow = false;
console.log("home=" +this.commonViewModel.showProgressBar());
}
}, error => {
console.log(error);
});
}
}
This is my service class which hold the value as true and false
#Injectable()
export class CommonViewModel {
progressBarShow: boolean = true;
public showProgressBar(): boolean {
return this.getProgressBarShow;
}
set setProgressBarShow(flag: boolean) {
this.progressBarShow = flag;
}
get getProgressBarShow(): boolean {
return this.progressBarShow;
}
}
The console output
In the console I,can see the output as True and False. But the app never hides as I can see the app component value is always true
Where I, am doing mistake. Can please someone let me know. I, dont want to use Input and Output to share the data.
Please let me know how can I resolve this issue.
it's possible that your parent component and your child component are being injected with two different instances of the service, depending on where you "provide" it. Try providing it from your app module.
Also, if the child is a direct child of the parent, you don't need the service, you can have an EventEmitter (an #Output) in child, and communicate through that.
See the documentation at https://angular.io/api/core/EventEmitter
I think, that GSSWain's answer must be work. If not, try use a getter
<mat-progress-bar *ngIf="isLoading"></mat-progress-bar>
get isLoading(){
return this.commonViewModel.showProgressBar();
}
I have created Angular2 + Typescript project. I have there alot of tables so I want to have something like base component for that.
There is my base component:
export abstract class ManagementComponent<T> implements BaseComponent {
protected selectedItem: T;
protected items: Array<T>;
}
Now there is my child component. I would like to get all items from http and then assign it into base class
export class CompetencesComponent extends ManagementComponent<Competence> implements OnInit {
thisField: string;
constructor(private service: CompetencesService) {
super();
}
ngOnInit(): void {
this.getCompetences();
}
private getCompetences() {
this.service.getCompetences().subscribe(function (competences: Array<Competence>) {
this.thisField // ok
this.items // not ok
})
}
}
Any idea how I can access base fields from subscribe methods?
Currently I'd expect that you wouldn't be able to reference either thisField or items, because you should be losing the this context inside your subscription function.
You can switch to an arrow function to retain context:
this.service.getCompetences().subscribe((competences: Array<Competence>) => { ... }
You can set list of competencies to parent class as follow:
private getCompetences() {
var self = this;
this.service.getCompetences().subscribe(function (competences: Array<Competence>) {
this.thisField // ok
self.items = competences; // not ok
})
}
The reason you are unable to access items property through this binding is the scope. Inside callback this binding is bound to something else and you loose the context.
I have some data that I'm populating to my Angular 2 app, and all is working as expected, EXCEPT, on occasion a page component loads before the information appears ready to display. On these occasions I can hit "refresh" and the data populates. However, this of course isn't the desired behavior. So my thought was to move the function responsible for calling this info into the ngOnInit lifecycle hook. However, my IDE shows an error when I try to do that, and I'm not sure why.
This is what relevant info of my current component looks like:
export class DoctorGeneralComponent extends EventHandler implements OnInit {
#Input('doctor')
public doctor: DoctorModel;
constructor(private dialog: MdDialog) {
super();
}
ngOnInit() {
}
public getPropertyStatus(name: string): string {
return this.doctor ? this.doctor.getPropertyStatus(name) : '';
}
}
If I move this "gePropertyStatus" method into my ngOnInit, like this:
ngOnInit() {
public getPropertyStatus(name: string): string {
return this.doctor ? this.doctor.getPropertyStatus(name) : '';
}
... I get a ts "declaration or statement expected" error.
Any idea what's going on here, and how I can resolve it to include this method within my ngOnInit lifecycle hook?
Call it inside of ngOnInit, define it outside of it:
ngOnInit() {
this.getPropertyStatus("whatever name");
}
private getPropertyStatus(name: string): string {
return this.doctor ? this.doctor.getPropertyStatus(name) : '';
}
I have 2 components: CommandListComponent and CommandLineComponent. Inside of a CommandListComponent template i handle a click event on a text string:
CommandListComponent template:
<li *ngFor="#command of commandList" class="b-command-list__command"><span (click)="checkCommand(command)" class="b-command-list__text">{{command}}</span></li>
commandlist.component.ts
import {CommandLineComponent} from "./commandline.component";
...
export class CommandListComponent {
commandLineComponent: any;
constructor(private _commandLine: CommandLineComponent) {
this.commandLineComponent = _commandLine;
}
checkCommand(command: string): void {
this.commandLineComponent.add(command);
}
}
When click is fired i pass choosen command to add method of a CommandLineComponent:
export class CommandLineComponent {
commands: string[] = [];
add(command: string): void {
if (command) this.commands.push(command);
console.log(this.commands);
}
}
And within a template of a CommandLineComponent i print a list of a commands with *ngFor:
<li *ngFor="#command of commands" class="b-command-textarea__command">{{command}}</li>
But *ngFor doesn't fires when i choose a command and commands array of a CommandLineComponent updated. So, data binding is not working. commands array updates successfully:
Thank you for help.
The problem is the way you reference the commandLineComponent component. If there is a relation between them you could use the ViewChild decorator
class CommandListComponent {
#ViewChild(CommandLineComponent)
commandLineComponent: any;
(...)
}
If not, you need to use a shared service to share the commands list between these two components. Something like that:
export class CommandService {
commands:string[] = [];
commandAdded:Subject<string> = new Subject();
add(command: string): void {
if (command) {
this.commands.push(command);
this.commandAdded.next(command);
}
console.log(this.commands);
}
}
You need to define the service when bootstrapping your application and both components can inject it.
class CommandListComponent {
constructor(private commandService:CommandService) {
}
}
checkCommand(command: string): void {
this.commandService.add(command);
}
The CommandLineComponent component will be notified of a new command like this and can update the view accordingly:
class CommandLineComponent {
constructor(private commandService:CommandService) {
this.commandService.commandAdded.subscribe(command => {
// Update the list displayed in the component...
});
}
}