Problems Moving Method within NgOnInit Life Cycle Hook in Angular 2 App - javascript

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

Related

DOM is not updated with EventListener using Angular 5

Context : I'm using an Angular PWA to communicate with an iOS native application through WKWebview. I'm using messageHandlers to be able to share data between typescript files and the Swift logic code.
Problem : I'm using addEventListener to listen a specific event on the window object. From my component, I subscribe to an observable to listen the change. But my component doesn't apply variable changes inside the subscribe method.
myService.ts
public myValue$ = new Subject<number>();
window.addEventListener('didDeviceDisconnected', (e) => {
...
this.dispatchInfo(someInfo);
});
private dispatchInfo(value: number) {
this.myValue$.next(value);
}
public getValue(): Observable<number> {
return this.myValue$.asObservable();
}
myComponent.ts
// Wait for the notification
this.myValueSubscription = this.myService.getValue().subscribe(value => {
this.myValue = value;
alert("myValue : " + this.myValue);
})
myComponent.html
{{ myValue }}
The alert displays correctly the value but the DOM shows that the value is undefined. I also tried to add setTimeout inside the subscribe function without success. How can I apply the change from the subscribe method ? Is it outside the angular scope ?
yes it is outside the angular scope.
you can try this..
myComponent.ts
import { Component , NgZone } from '#angular/core';
......
constructor(public ngZone: NgZone)
......
this.myValueSubscription = this.myService.getValue().subscribe(value => {
this.ngZone.run(()=> {
this.myValue = value;
});
})
I resolved this issue by using ChangeDetectorRef which provides change detection functionality.
import { Component, ChangeDetectorRef } from '#angular/core';
constructor(.., private cdr: ChangeDetectorRef){}
this.myValueSubscription = this.myService.getValue().subscribe(value => {
this.myValue = value;
alert("myValue : " + this.myValue);
this.cdr.detectChanges(); // <= ADDED
})

httpClient.get is undefined when using dynamic function/observable references

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

Data sharing between component with data servive not working in angular 5

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

Unable to get variable outside subscribe in Angular

Well, maybe my issue sounds similar to the few questions here about Angular2-EventEmitter-Subject-Observable-Subscribe-stuff... but it's quite different.
My goal (in short):
Be able to go from route ItemsList to route AddNewItem and then push on the custom 'back' button and find myself in ItemsList WITH ALL SETTINGS THAT I HAD HERE. For a example - search params. So... the only way (afaik) in Angular2+ to store that params between non-related components is to use shared service. So... My code:
ItemsList ts\html (very briefly):
export class ListComponent implements OnInit {
public addItem(allSettings: ISettingsTable) {
this._shService.returnToList = true;
this._shService.allCurrentSettings = allSettings;
this._router.navigate(['someRoute', 'add']);
}
ngOnInit() {
this._settings = {
search: /* .. */,
/* .. */
}
this._shService.getSettings().subscribe((settings: ISettingsTable) => {
this._settings = settings;
}
}
}
<button (click)='addItem(_settings)'>
<mat-icon svgIcon='add'></mat-icon>
</button>
Service (in short):
export class SettingsService {
private _returnToList: boolean;
private _settingsChanged$: Subject<ISettingsTable> = new Subject();
public emitSettings(val: ISettingsTable) {
this._settingsChanged$.next(val);
}
public getSettings(): Subject<ISettingsTable> {
return this._settingsChanged$;
}
}
AddEditItems ts\html (very briefly as well):
export class EditComponent implements OnInit {
public back(par: ISettingsTable): void {
if (this.returnToList) {
this._router.navigate(['someRoute', 'list']).then(() => {
this._shService.emitSettings(par);
});
ngOnInit() {
if (this._shService.returnToList) {
this.returnToList = true;
this._p = this._testS.allCurrentSettings;
}
}
}
<button mat-raised-button (click)='back(_p)'>Go back</button>
So! When I go to the Add route I store the current params in service. Fine. Then on the Add route I get them from service. Fine. Then when I wanna go back I pass that params to the emit method of Subject\Event Emitter... Fine! But on the List route this._settings exists only INSIDE subscribe... It's undefined outside. Yeah, I know about asynchronous behavior and stuff... But how to resolve my issue properly??? Anyone! Please. Hope I made myself clear.
EDIT...
Obviously I always on all routes have some settings. The issue is I dunno how to code If I've got some settings from observable - use them, if not - use default this._settings ...

how can I listen to changes in code in angular 2?

I'm using angular 2. I have a component with an input.
I want to be able to write some code when the input value changes.
The binding is working, and if the data is changed (from outside the component) I can see that there is change in the dom.
#Component({
selector: 'test'
})
#View({
template: `
<div>data.somevalue={{data.somevalue}}</div>`
})
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
dataChagedListener(param) {
// listen to changes of _data object and do something...
}
}
You could use the lifecycle hook ngOnChanges:
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
ngOnChanges([propName: string]: SimpleChange) {
// listen to changes of _data object and do something...
}
}
This hook is triggered when:
if any bindings have changed
See these links for more details:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html
As mentioned in the comments of Thierry Templier's answer, ngOnChanges lifecycle hook can only detect changes to primitives. I found that by using ngDoCheck instead, you are able to check the state of the object manually to determine if the object's members have changed:
A full Plunker can be found here. But here's the important part:
import { Component, Input } from '#angular/core';
#Component({
selector: 'listener',
template: `
<div style="background-color:#f2f2f2">
<h3>Listener</h3>
<p>{{primitive}}</p>
<p>{{objectOne.foo}}</p>
<p>{{objectTwo.foo.bar}}</p>
<ul>
<li *ngFor="let item of log">{{item}}</li>
</ul>
</div>
`
})
export class ListenerComponent {
#Input() protected primitive;
#Input() protected objectOne;
#Input() protected objectTwo;
protected currentPrimitive;
protected currentObjectOne;
protected currentObjectTwo;
protected log = ['Started'];
ngOnInit() {
this.getCurrentObjectState();
}
getCurrentObjectState() {
this.currentPrimitive = this.primitive;
this.currentObjectOne = _.clone(this.objectOne);
this.currentObjectTwoJSON = JSON.stringify(this.objectTwo);
}
ngOnChanges() {
this.log.push('OnChages Fired.')
}
ngDoCheck() {
this.log.push('DoCheck Fired.');
if (!_.isEqual(this.currentPrimitive, this.primitive)){
this.log.push('A change in Primitive\'s state has occurred:');
this.log.push('Primitive\'s new value:' + this.primitive);
}
if(!_.isEqual(this.currentObjectOne, this.objectOne)){
this.log.push('A change in objectOne\'s state has occurred:');
this.log.push('objectOne.foo\'s new value:' + this.objectOne.foo);
}
if(this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)){
this.log.push('A change in objectTwo\'s state has occurred:');
this.log.push('objectTwo.foo.bar\'s new value:' + this.objectTwo.foo.bar);
}
if(!_.isEqual(this.currentPrimitive, this.primitive) || !_.isEqual(this.currentObjectOne, this.objectOne) || this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)) {
this.getCurrentObjectState();
}
}
It should be noted that the Angular documentation provides this caution about using ngDoCheck:
While the ngDoCheck hook can detect when the hero's name has changed,
it has a frightful cost. This hook is called with enormous frequency —
after every change detection cycle no matter where the change
occurred. It's called over twenty times in this example before the
user can do anything.
Most of these initial checks are triggered by Angular's first
rendering of unrelated data elsewhere on the page. Mere mousing into
another input box triggers a call. Relatively few calls reveal actual
changes to pertinent data. Clearly our implementation must be very
lightweight or the user experience will suffer.

Categories

Resources