How to use the "window" property in Angular? - javascript

I'm trying to implement this "Scroll back to top" button found here:
https://www.w3schools.com/howto/howto_js_scroll_to_top.asp
I'm new to Angular and my attempts to implement this keep getting me errors and type errors.
This is my code:
home.component.html
<div class="country-card-container">
<button (click)="topFunction()" class="scroll-top">TOP</button>
<app-country-card *ngFor="let country of displayCountries" [country]="country" class="country-cards"></app-country-card>
</div>
home.component.ts
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.sass']
})
export class HomeComponent {
mybutton = document.getElementsByClassName("scroll-top");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = this.scrollFunction();
scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
this.mybutton.style.display = "block";
} else {
this.mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
topFunction() {
document.body.scrollTop = 0; // For Safari
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
}
}
I'm getting the errors:
Unexpected keyword or identifier.ts(1434)
Member 'window' implicitly has an 'any' type.ts(7008)
Cannot find name 'scrollFunction'.ts(2304)
Property 'style' does not exist on type 'HTMLCollectionOf<Element>'.ts(2339)
I also tried putting
window.onscroll = this.scrollFunction();
in ngOnInit like this:
ngOnInit(){
window.onscroll = this.scrollFunction();
}
but that doesn't work either.
How do I implement this? What did I do wrong and how do you fix it?

Well, it looks like the problem is that you try to declare a window property of your component by doing this:
export class HomeComponent {
// this code is not valid, because you try to declare a class property here, not to get the window reference
window.onscroll = this.scrollFunction();
}
Angular has a directive made specially for those purposes, called #HostListener. I would recommend you to consider it.
#Component({...})
export class HomeComponent {
#HostListener('window:scroll', ['$event'])
onWindowScroll(event: Event) {
// do whatever you want here, including manipulations with the window object as it's available here
console.log(window);
}
}
Here is a reference to the official docs: https://angular.io/api/core/HostListener
Good luck :)

Related

How to access function from parent component when in modal component?

The refreshTable() function is in the parent component. I need to trigger it whenever I updated information in my modal and closes it.
I am using #ng-bootstrap for my modal
For may parent.component.ts
import { NgbModal } from '#ng-bootstrap/ng-bootstrap';
constructor(
private ngbModal: NgbModal,
)
viewModal() {
const openModal = this.ngbModal.open(ModalComponent);
openModal .componentInstance.id = row.id;
}
refreshTable() {
//refresh code block here
}
For my modal.component.ts
import { NgbActiveModal} from '#ng-bootstrap/ng-bootstrap';
constructor(
private activeModal: NgbActiveModal,
)
updateParent() {
//update data to database code here
this.activeModal.close();
}
How to trigger refreshTable() from ModalComponent after closing the modal? Since there are changes in the data from database, data from parent is not updated accordingly when modal is closed.
Try to change to this
const openModal = this.ngbModal.open(ModalComponent);
openModal.componentInstance.id = row.id;
openModal.dismissed.subscribe(
_ => {
this.refreshTable();
}
)
in parent.component.ts
Pass it from the parent component to the child component as a prop.
Use bind or an arrow function to preserve the this value.
Add an output event to your modal component
#Output() onCLose: EventEmitter<any> = new EventEmitter();
When the user clicks close just call the emit() method
this.onCLose.emit(value); //value can be whatever you want to pass to the parent
Update your viewModal method in the parent to include a subscription to the close event
viewModal() {
const openModal = this.ngbModal.open(ModalComponent);
openModal.componentInstance.id = row.id;
openModal.componentInstance.onClose.subscribe(value =>
// handle the close event
this.refreshTable();
})
}
You could also do it any other way communication between components can be achieved:
service
state management (ngrx, etc)
ngBootstrap modals have a beforeDismiss event, to which you can add logic before you close the modal. You have to return a truthy value for the modal to actually close.
You could do it like this:
const openModal = this.ngbModal.open(ModalComponent, { beforeDismiss: () => this.updateParent() }); // if modal is not closing, your function is returning false.
Check here for info on that function (do use ctrl F to find the option if needeD)
Edit: #tom-fong's answer is also a good solution, but you'd have to check if you're using version > 8.0.0

Removing full screen event listener in ngOnDestroy

Can't remove fullscreenchange event listener in ngOnDestroy Angular 6
I've tried calling the .removeEventListener() in ngOnDestroy which doesn't remove the events. I have also tried calling the removeEventListeners in a function after a 10 second timeout, and the events still continue to be triggered after.
imports
import { DOCUMENT } from '#angular/common';
import { Component, HostBinding, Inject, OnInit, OnDestroy } from '#angular/core';
Component code
elem: any;
constructor(private framesService: FramesService, private route: ActivatedRoute,
#Inject(DOCUMENT) private document: Document) { }
ngOnInit() {
this.elem = this.document.documentElement;
this.document.addEventListener('fullscreenchange', this.onFullscreenChange.bind(this));
this.document.addEventListener('mozfullscreenchange', this.onFullscreenChange.bind(this));
}
onFullscreenChange(event: Event) {
event.preventDefault();
console.log('Fullscreen event fired');
}
onViewFrame() {
if (this.elem.requestFullscreen) { // Chrome
this.elem.requestFullscreen();
} else if (this.elem.mozRequestFullScreen) { // Firefox
this.elem.mozRequestFullScreen();
}
}
ngOnDestroy() {
this.document.removeEventListener('fullscreenchange', this.onFullscreenChange.bind(this));
this.document.removeEventListener('mozfullscreenchange', this.onFullscreenChange.bind(this));
}
The onViewFrame() function is tied to a click event from a button on the page.
Every time this component is constructed, the events are added, but they are never removed. So if this component is loaded 3 times during a browsing session on the page, it will trigger the console.log three times every time full screen is initiated or the ESC key is used to exit full screen. Would like the events to be removed upon leaving so that they may be re-registered properly the next time.
this.onFullscreenChange.bind(this) creates a new reference to that function. You need to pass the same reference to addEventListener and removeEventListener.
elem: any;
fsEventHandler: any = this.onFullscreenChange.bind(this); // <- Here
ngOnInit() {
this.elem = this.document.documentElement;
this.document.addEventListener('fullscreenchange', this.fsEventHandler);
this.document.addEventListener('mozfullscreenchange', this.fsEventHandler);
}
ngOnDestroy() {
this.document.removeEventListener('fullscreenchange', this.fsEventHandler);
this.document.removeEventListener('mozfullscreenchange', this.fsEventHandler);
}
See MDN for more explanation.
Since you are using Angular you can use RxJS to achieve the same behavior.
You can create Observable using fromEvent
fromEvent(this.document, 'fullscreenchange');
To trigger some function you need to add .pipe() with tap operator, to activate it you also need to subscribe to it. Also save the subscription to be able to unsubscribe inside of ngOnDestroy()
ngOnInit() {
this.elem = this.document.documentElement;
console.log(this.document);
this.fullScreen = fromEvent(this.document, 'fullscreenchange').pipe(
tap(this.onFullscreenChange.bind(this))
).subscribe();
}
onFullscreenChange(event: Event) {
event.preventDefault();
console.log('Fullscreen event fired');
}
ngOnDestroy() {
this.fullScreen.unsubscribe();
}

Angular 4 load event with #HostListener

This is what I am trying to do:
import { Directive, HostListener, Input } from '#angular/core';
#Directive({
selector: '[appResizeWindow]'
})
export class ResizeWindowDirective {
#Input('appResizeWindow') line_ChartOptions: {};
constructor() { }
#HostListener('window:resize', ['$event']) onResize(event: Event) {
console.log('Yo');
if (event.target['innerWidth'] < 420)
this.line_ChartOptions['hAxis']['format'] = 'MMM';
else if (event.target['innerWidth'] < 760)
this.line_ChartOptions['hAxis']['format'] = 'MM. yy\'';
else this.line_ChartOptions['hAxis']['format'] = 'MMM d, yyyy';
}
#HostListener('load', ['$event']) onPageLoad(event: Event) {
console.log('loaded');
this.onResize(event.target['innerWidth']);
}
}
So 'window.resize' works perfect when I attack in in the template.
The problem is with load. I event tried onload
I want the page to execute when the page is loaded.
What am I missing here?
The load event has already happened before your component/directive is even initialized.
Just add your code to ngAfterViewInit()
If your component/directive is removed and re-added after it was first initialized, you need to take care yourself that the code isn't executed more than once by using a global service or a static variable to store the status.
try this one, this will work.
#HostListener('window:load',['$event'])
onPageLoad(event: Event) {
console.log('loaded',event);
}
i solved this using OnInit
import { Directive, HostListener, Input, OnInit } from '#angular/core';
inside your component class put this..
ngOnInit() {
// Code to be executed on Init
}

How to use RxJS in angular2?

Is there any way to write a code like that in angular 2?
var closeButton1 = document.querySelector('.close1');
var close1ClickStream = Rx.Observable.fromEvent(closeButton1, 'click');
I have tried many ways to put them in angular 2 component put it does not worked well!
I tried something like this
component.ts
#ViewChild('delete') closeButton1: ElementRef;
close1ClickStream = Observable.fromEvent(closeButton1, 'click');
component.html
<!-- this code is inside *ngFor -->
<li><a #delete [routerLink]="['/update', item.id]">delete</a></li>
The problem is that I can not access the element even if I used AfterContentInit. Moreover, if I could access it, can I use the Observable.fromEvent(...)?
Use directives to handle events
#Directive({
selector: '[click-handler]'
})
export class ClickHandler {
constructor(public elementRef: ElementRef,) {
}
#HostListener('click', ['$event'])
onClick(e) {
// do staff here
}
}
usage
<button click-handler> click </button>
Anyway if you want to do it using Observables you will need
elementRef.nativeElement which is available here , just implement OnInit method and you are good to go.
Yes you can do it :
import { Observable } from 'rxjs/Observable';
//...
#ViewChild('delete') closeButton1: ElementRef;
close1ClickStream = Observable.fromEvent(closeButton1, 'click');

Change view properties EventAggregator Aurelia

import {inject, bindable, customElement} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
#customElement('add-company-modal')
#inject(EventAggregator)
export class AddCompanyModal {
constructor(eventAggregator) {
this.eventAggregator = eventAggregator;
}
attached() {
console.log('attached add company modal');
this.eventAggregator.subscribe('add-company-modal-toggle', open => {
console.log('getting hit');
if (open) this.open();
else this.close();
});
}
open() {
this.activate = true;
}
close() {
this.activate = false;
}
}
Am I doing this correctly? Basically I have another view model which is publishing events. the console log is getting read out. The idea is that activate is bound to a class:
<div class="modal-backdrop ${activate ? 'modal-show' : ''}" click.trigger="close()">
<div class="modal">
<div class="modal-title">
<h1>Add Company <span class="modal-close">x</span></h1>
</div>
<div class="modal-body modal-no-footer">
<add-company>
</add-company>
</div>
</div>
</div>
This is wrapped in a template. However, this doesn't show at all. It works if I have a bindable property, but only when you click open on the other vm. When you close the modal you can't reopen it again, hence why I am using the event aggregator.
So the console.log is getting hit, I know that I have set up the event aggregation correctly, I just have a feeling that the framework isn't picking this up. I know if this was angular then it could be outside the digest cycle but I know aurelia doesn't work like that.
Reproduction
I've created a small repo to reproduce the issues on my Github
activate might sound better as activated or isActivated.
Just FYI there is a life-cycle method the router will call that is named activate so just trying to let you know of that possible collision.
If that doesn't help the next step for me would be to log the value of open inside the subscribe event. I would also verify that it's not failing with a method undefined error or anything.
Last, I personally like to define the properties either on the class or in the constructor. It's possible that you are binding to undefined so it doesn't catch the value change.
export class AddCompanyModal {
isActivated = false;
constructor(eventAggregator) {
this.eventAggregator = eventAggregator;
}
attached() {
console.log('attached add company modal');
this.eventAggregator.subscribe('add-company-modal-toggle', open => {
console.log('getting hit');
if (open) this.open();
else this.close();
});
}
open() {
this.isActivated = true;
}
close() {
this.isActivated = false;
}
}
The issue was with the value coming through to the subscribe callback was always false.
It was an error else where in my code.

Categories

Resources