Angular 2+ window.onfocus and windows.onblur - javascript

So I need in Angular 2 or 4 to manage when the browser tab of my app is focused or not. Is there any way to use the window.onfocus and window.onblur ?
Thanks a lot

You can use a component with #HostListener.
Something like:
#Component({})
export class WindowComponent {
constructor(){}
#HostListener('window:focus', ['$event'])
onFocus(event: any): void {
// Do something
}
#HostListener('window:blur', ['$event'])
onBlur(event: any): void {
// Do something
}
}
Just check that you don't have multiple WindowComponent running at the same time, because you will have an unexpected behavior, due that each instance will react to these events.

Turns out this doesn't work in services, which was my requirement. My solution was doing it "the old way":
#Injectable()
export class WindowService {
constructor(){
window.addEventListener('focus', event => {
console.log(event);
});
window.addEventListener('blur', event => {
console.log(event);
});
}
}
Not sure I did it the "correct" way, but it works on Chrome. What I'm not sure about is if I should destroy the event listener or not, and if it works in other browsers. Let me know if I'm inadvertently shooting myself in the foot here. Will update answer if so, or delete it if need be.

In a more reactive approach, I use this injection token:
export const WINDOW_FOCUS = new InjectionToken<Observable<boolean>>(
'Shared Observable based on `window focus/blurred events`',
{
factory: () => {
return merge(fromEvent(window, 'focus'), fromEvent(window, 'blur')).pipe(
startWith(null),
map(() => window.document.hasFocus()),
distinctUntilChanged(),
share(),
);
},
},
);
Ideally, you do not want to rely on the global windows variable, you could replace it with injecting the WINDOW and DOCUMENT tokens from https://github.com/ng-web-apis/common.
To use the WINDOW_FOCUS injection token, in any component or service, it can be added to the constructor like this:
#Injectable()
export class SomeService {
constructor(
#Inject(WINDOW_FOCUS) private readonly windowFocus$: Observable<boolean>
) {}
}

Related

Typescript control flow behavior

I am new to JS, TS and Angular...
So I have this angular component:
export class AdminProductsMenuComponent implements OnInit{
constructor(private productService: ProductService,
private alertService: AlertService,
private router: Router) {
this.subscribeToDeleteProductEvents();
}
productsAdminModel: IGetProductAdminModel[] = [];
private productId: string;
ngOnInit(): void {
this.executeGetAllProductsAsAdmin();
}
executeGetAllProductsAsAdmin() {
this.productService.getAllProductsAsAdmin().subscribe({
next: (productData) => this.productsAdminModel = productData
})
}
private subscribeToDeleteProductEvents() {
this.alertService.getSubjectAlertEvent().subscribe({
next: (isConfirmed) => {
if (isConfirmed) {
this.productService.deleteProduct(this.productId).subscribe({
next: () => {
this.reloadCurrentResources();
}
});
}
}
});
}
private reloadCurrentResources(): void {
// save current route first
this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
this.router.navigate(['/AdminProducts']); // navigate to same route
});
}
executeProductDelete(id: string) {
this.productId = id;
this.alertService.confirmationAlertProductDelete();
}
}
Brief explanation:
I have subscription in the constructor which listens for events during the lifetime of the component.
An event is fired when the last method is called (through the template) which prompts a SweetAlert confirm dialog. Depending on the selected the event is true or false.
Now here is the tricky part - if I move the executeProductDelete() method above reloadCurrentResources() and subscribeToDeleteProductEvents() and invoke it (executeProductDelete) it will complete the action and throw error
I have a feeling that it executes again the subscribeToDeleteProductEvents() and reloadCurrentResources() .
If I move the executeDeleteProduct() as the last method, no error occurs.
Why is this behavior? I have a feeling that they continue to run synchronously. They are not invoked anywhere else.
There seems to be 2 main problems there:
Avoid at all costs "reloading" the same component, try to abstract the reload logic into methods. This could cause weird issues and unecessary loads, as the SPA is meant to be a single page application.
Since you are problably re-instancianting the component over and over again through your reloadResources, the alert service behaviour subjects creates new subscriptions. And since you haven't unsubscribed from them, they will keep listening forever.

Call component logic when state changes in ngrx

I'm currently developing an application with Angular using redux principle with ngrx.
I'm looking for a best practice for reacting to state changes and call some component logic depending on this state. I'll give you an (simplified) example to make clear what I mean:
reducers.ts
import {createSelector} from 'reselect';
export const getViewTypeOrFilterChanged = createSelector(isLoading, getActiveViewType, getActiveFilter, (isLoading, activeViewType, activeFilter) => {
// ensure that data is loaded
if (!isLoading) {
return {
activeViewType: activeViewType,
activeFilter: activeFilter
};
}
});
example-component.ts
#Component({ ... })
export class ExampleComponent implements OnInit {
// properties ...
constructor(private store: Store<fromRoot.AppState>) {
}
ngOnInit() {
this.subscriptions.push(
this.store.select(fromRoot.getViewTypeOrFilterChanged).subscribe((result) => {
if (result) {
this.property1 = result.activeType;
this.dependentHelperClass.method1(result.activeFilter);
this.method1();
this.method2(result.activeFilter);
this.method3();
}
})
);
}
ngOnDestroy() {
this.subscriptions.forEach((subscription: Subscription) => {
subscription.unsubscribe();
});
}
// methods ...
}
As you can see I'm also using reselct to combine three different slices of state within a selector (getViewTypeOrFilterChanged). In the subscription to this selector I then want to take some actions according to the combined state.
The thing is, I'm feeling like using ngrx store and subscriptions more in a way of publish/subscribe pattern here and it feels not quite correct. Also the subscriptions (I have multiple ones) in ngOnInit and unsubscriptions in ngOnDestroy bother me, but I can't think of a way achieving the same results using e.g. async pipe.
Is there maybe a more elegant way of reacting to (combined) state changes?
Thanks!
With RxJS you should think of everything as a stream - the following code is just as an example, because I don't really know any of your UI-logic so just look at the structure and not at the logic of the code, since it's more like a very wild guess of mine:
#Component({ ... })
export class ExampleComponent implements OnInit {
private destroyed$ = new Subject<boolean>();
// the following streams can be used in the controller
// as well as in the template via | async
// the .share() is just so the | async pipe won't cause unneccessary stream-creations (the result should be the same regardless of the .share(), it's just a minor performance-enhancement when using multiple | async)
isLoading$ = this.store.select(fromRoot.isLoading).share();
activeViewType$ = this.store.select(fromRoot.getActiveViewType).share();
activeFilter$ = this.store.select(fromRoot.getActiveFilter).share();
activeViewTypeAndFilter$ = Observable.combineLatest(this.activeViewType$, this.activeFilter$).share();
constructor(private store: Store<fromRoot.AppState>) {
}
ngOnInit() {
this.isLoading$
.filter(isLoading => !isLoading) // the initial stream will not emit anything until "loading" was false once
.switchMapTo(this.activeViewTypeAndFilter$)
.do([viewType, filter] => {
this.dependentHelperClass.method1(activeFilter);
this.method1();
this.method2(activeFilter);
this.method3();
})
.takeUntil(this.destroyed$) //this stream will automatically be unsubscribed when the destroyed$-subject "triggers"
.subscribe();
}
ngOnDestroy() {
this.destroyed$.next(true);
this.destroyed$.complete();
}
// methods ...
}
As I said: logic-wise I cannot say if this is what you need, but that's just a question of using different operators and/or a different order to arrange your "main-stream" differntly.

Angular2: Binding and Callbacks

I'm trying to create a small Directive to capture the windows global-keyup and then invoke a callback, so I basically captue the global window in a service and the keyup on my Directive:
export class EnterActivationDirective implements OnInit {
private _enterClicked: Action;
#Input() public set enterClicked(action: Action) {
this._enterClicked = action;
}
constructor(public el: ElementRef, public windowWrapperService: WindowWrapperService) {
}
ngOnInit() {
this.windowWrapperService.nativeWindow.onkeyup = this.onWindowKeyUp.bind(this);
}
private onWindowKeyUp(event: any) {
if (event.code === 'Enter' && this._enterClicked) {
this._enterClicked();
}
}
}
The Service and Action-Type aren't that interesting, since the Service just passes the native window and the Action-Type is a generic Callback without any parameters or return-value.
The logic itself works, but I get some weird effects regarding the binding to the action. So, one of my other Components registers to the Directive:
<div appEnterActivation [enterClicked]="onKeyUp.bind(this)">
<div>
... Amended
Which then triggers a search-operation:
public search(): void {
this.searchInProgress = true;
const param = this.createSearchParams();
this.searchStarted.emit(param);
this.timeReportEntryApiService.searchTimeReportEntries(param)
.then(f => {
const newObjects = ArrayMapper.MapToNewObjects(f, new TimeReportEntry());
this.searchFinished.emit(newObjects);
this.searchInProgress = false;
}).catch(f => {
this.searchInProgress = false;
throw f;
});
}
public get canSearch(): boolean {
return this.form.valid && !this.searchInProgress;
}
public onKeyUp(): void {
debugger ;
if (this.canSearch) {
this.search();
}
}
Not too much logic here, but if the search is started from the callback, it seems like the properties and functions are in place, but they are on some kind of different object:
The searchInProgress-property is set tu true, but on the second enter, it is false again
I have some animations and bindings in place, none of them are triggered
Since everything is working with a plain button, I'm almost certain it kindahow has to do with the callback and the binding to this.
I researched a bit regarding this bind, but regarding this thread Use of the JavaScript 'bind' method it seems to be needed. I also tested without binding, but then the this is bound to the global window variable.
Why are you using an #Input? Angular made #Output for such a use case:
template:
<div appEnterActivation (enterClicked)="onEnter()"></div>
class:
export class EnterActivationDirective implements OnInit {
#Output()
public readonly enterClicked: EventEmitter<any> = new EventEmitter();
#HostBinding('document.keyup.enter')
onEnter(): void {
this.enterClicked.emit();
}
}
No need for difficult checks or wrappers :)
Since you are using TypeScript you can use arrow function, that manages this correctly.
public onKeyUp = () => {
debugger ;
if (this.canSearch) {
this.search();
}
}
In that case you can just setup the property binding as
[enterClicked]="onKeyUp"

Transactional model update in Angular 2

I previously worked with React/Mobx with the action concept. This allows to change some model properties in one transaction without firing multiple events to update UI state (only one event will be triggered after an action method will be executed).
Is there any approach or may be patterns to achieve the same behavior in Angular 2?
I'm using a service like this to control UI:
#Injectable()
export class UIService {
private buffer: any = {};
private dispatcher: Subject<any> = new Subject();
constructor() {
this.dispatcher
.asObservable()
.map(state => this.buffer = { ...this.buffer, ...state })
.debounceTime(50)
.subscribe(() => { /* do something */));
}
set(key: string, value?: any) {
this.dispatcher.next({ [key]: value });
}
}
and in different components in ngOnInit() I set different options:
this.uiService.set('footer', false); // in base component
this.uiService.set('footer', true); // in extended component
this.uiService.set('sidebar', true); // in other component
this.uiService.set('title', 'My Page'); // elsewhere...
This way I have only one object that reflects my current UI state...
Note that MobX can be used with Angular 2 as well: https://github.com/500tech/ng2-mobx

Angular 2.x watching for variable change

I'm migrating from angular 1.x to 2.x but my brains still think in angular 1.x so sorry for silly questions.
What I need is to take some action when one of my scope variables component properties changes. I found a solution but I think there should be better solution
export class MyApp {
router: Router;
location: Location;
fixed: boolean = true;
private set isFixed(value:boolean) {
this.fixed = value;
//TODO: look here
console.log('isFixed changed', value);
}
private get isFixed():boolean {
return this.fixed;
}
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
Look at the line console.log('isFixed changed', value); It's what I need and it's working. But I made it by declaring getter and setter, but isn't there a better solution to watch variables? Like in angular 1.x was $scope.$watch?
I think my component code should look like
export class MyApp {
router: Router;
location: Location;
isFixed: boolean = true;
//TODO: $watch for isFixed change {
console.log('isFixed changed', value);
// }
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
You might want to implement the OnChanges interface and implement the ngOnChanges() method.
This method is called whenever one of the components input or output binding value changes.
See also https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
Dart code example
#Input() bool fixed;
#override
void ngOnChanges(Map<String, SimpleChange> changes) {
print(changes);
}
You might find this answer to Delegation: EventEmitter or Observable in Angular2 helpful (worked for me).
Essentially you could use a BehaviorSubject, which allows you to set an initial value for the property you're interested in, then subscribe to changes to that property wherever that service is injected.
e.g.
export class SomeService {
private fixed = new BehaviorSubject<boolean>(true); // true is your initial value
fixed$ = this.fixed.asObservable();
private set isFixed(value: boolean) {
this.fixed.next(value);
console.log('isFixed changed', value);
}
private get isFixed():boolean {
return this.fixed.getValue()
}
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
Then in a class (e.g. Component) that's interested in the fixed value:
export class ObservingComponent {
isFixed: boolean;
subscription: Subscription;
constructor(private someService: SomeService) {}
ngOnInit() {
this.subscription = this.someService.fixed$
.subscribe(fixed => this.isFixed = fixed)
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Update value:
export class Navigation {
constructor(private someService: SomeService) {}
selectedNavItem(item: number) {
this.someService.isFixed(true);
}
}
To auto get updated value by this service
NOTE: I tested it in Angular 9
my service file
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SharedService {
private fixed= new BehaviorSubject<boolean>(false);
fixed$ = this.fixed.asObservable();
constructor() {}
updateFixedValue(value: boolean) {
this.fixed.next(value);
console.log('fixed changed', value);
}
}
Now you can get value in any component (within ngOnInit or anywhere you want) like below
NOTE: this value will be change automatically after update
this.sharedService.fixed$.subscribe(val=>{ this.isFixed = val; });
and you can update or set new value from any component like below
this.sharedService.updateFixedValue(your_boolean_value);
Thanks, I hope it's work for you.
See Angular2 Component Interaction (has code examples).
The short answer to your question is that it really just depends on what you are trying to do. Even then, there are multiple ways to do what you want to do even if it's not really intended for it. So, I think it's best if you just take a few minutes to look at their documentation about Component Interaction and Forms.
My personal preference is to use events when a property has changed. The ngOnChanges event can be used for this but I prefer to work with #Input and #Output, and form value changed events (Angular2 Forms).
Hope this helps and gives you a direction you want to take.
I think it is possible to use simple getter and setter for this purpose.
class Example {
private _variable: string = "Foo";
set variable(value: string) {
this._variable = value;
console.log("Change detected: ", this.variable);
}
get variable(): string {
return this._variable;
}
}
let example = new Example();
console.log(example.variable);
example.variable = "Bar";
console.log(example.variable);
And output will be:
Foo
Change detected: Bar
Bar

Categories

Resources