angular 2+ vertically resize div
Solution 1
I think you miss the part where you keep the old value of the height, plus check the state on mouseup
and also to listen to mousedown
. I didn't make it to a directive in the example bellow, but it would be complicated.
Typescript:
height = 150;
y = 100;
oldY = 0;
grabber = false;
@HostListener('document:mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
if (!this.grabber) {
return;
}
this.resizer(event.clientY - this.oldY);
this.oldY = event.clientY;
}
@HostListener('document:mouseup', ['$event'])
onMouseUp(event: MouseEvent) {
this.grabber = false;
}
resizer(offsetY: number) {
this.height += offsetY;
}
@HostListener('document:mousedown', ['$event'])
onMouseDown(event: MouseEvent) {
this.grabber = true;
this.oldY = event.clientY;
event.preventDefault();
}
HTML
<div class="textarea" [style.height.px]='height' contenteditable="true" >
this is a text area
<div class="grabber"></div>
</div>
Solution 2
Using @Vega's Answer - a directive.
import { Directive, HostListener, ElementRef, OnInit } from '@angular/core';
@Directive({
selector: '[resizer]'
})
export class NgxResizerDirective implements OnInit {
height: number;
oldY = 0;
grabber = false;
constructor(private el: ElementRef) { }
@HostListener('document:mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
if (!this.grabber) {
return;
}
this.resizer(event.clientY - this.oldY);
this.oldY = event.clientY;
}
@HostListener('document:mouseup', ['$event'])
onMouseUp(event: MouseEvent) {
this.grabber = false;
}
resizer(offsetY: number) {
this.height += offsetY;
this.el.nativeElement.parentNode.style.height = this.height + "px";
}
@HostListener('mousedown', ['$event']) onResize(event: MouseEvent, resizer?: Function) {
this.grabber = true;
this.oldY = event.clientY;
event.preventDefault();
}
ngOnInit() {
this.height = parseInt(this.el.nativeElement.parentNode.offsetHeight);
}
}
HTML
<div class="textarea" contenteditable="true">
this is a text area
<div class="grabber" resizer contenteditable="false" ></div>
</div>
Solution 3
Try this:
Add a variable:
private canResize = false;
On mousedown enable resize by setting canResize to true:
@HostListener('mousedown', ['$event']) enableResize(e) {
this.canResize = true;
event.preventDefault();
}
so that you resize only when mouse is down:
@HostListener('window:mousemove', ['$event']) resize(e) {
if (this.canResize) {
this.el.nativeElement.parentNode.style.height = (e.clientY - this.el.nativeElement.parentNode.offsetTop) + 'px';
}
event.preventDefault();
}
On mouse up set canResize to false to disable resizing:
@HostListener('window:mouseup', ['$event']) stopResize(e) {
this.canResize = false;
event.preventDefault();
}
Also, take a look at this
(UPDATE: created stackblitz)
Solution 4
I found this library extremely useful and easy to use : angular-split
Usage is as given below (Below example is taken from an app component with a navigation menu on the left and individual components/pages are rendered on right - A highly used use case if you like a vertical navigation menu.)
<as-split direction="horizontal" style="height: 1000px;">
<as-split-area size="15">
<app-nav-menu></app-nav-menu>
</as-split-area>
<as-split-area size="85">
<div class="col-12 col-lg-9 body-content">
<router-outlet></router-outlet>
</div>
</as-split-area>
</as-split>
Solution 5
I've little bit improved @Sibiraj's and @Vega's answers. The main issue with the suggested solution is performance:
@HostListener('document:mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
if (!this.grabber) {
return;
}
this.resizer(event.clientY - this.oldY);
this.oldY = event.clientY;
}
You have to understand that @HostListener('document:mousemove', ['$event'])
will be fired every mouse move on your document, you can avoid this adding mousemove
listener after mousedown
event and cancel it after 'mouseup' event. Please see my version:
import {Directive, ElementRef, HostListener, OnDestroy, OnInit} from '@angular/core';
import {fromEvent, Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
@Directive({
selector: '[resizer]'
})
export class ResizeDirective implements OnInit, OnDestroy {
height: number;
oldY = 0;
grabber = false;
destroy$ = new Subject();
constructor(private el: ElementRef) { }
ngOnInit() {
this.height = parseInt(this.el.nativeElement.parentNode.offsetHeight, 10);
}
@HostListener('document:mouseup', ['$event'])
onMouseUp(): void {
this.grabber = false;
this.destroy$.next();
}
@HostListener('mousedown', ['$event']) onResize(event: MouseEvent, resizerCallback?: Function) {
this.grabber = true;
this.oldY = event.clientY;
event.preventDefault();
this.addMouseMoveListener();
}
resizer(offsetY: number): void {
this.height += offsetY;
this.el.nativeElement.parentNode.style.height = this.height + 'px';
}
addMouseMoveListener(): void {
fromEvent(document, 'mousemove')
.pipe(takeUntil(this.destroy$))
.subscribe(this.mouseMoveCallback.bind(this));
}
mouseMoveCallback(event: MouseEvent): void {
if (!this.grabber) {
return;
}
this.resizer(event.clientY - this.oldY);
this.oldY = event.clientY;
}
ngOnDestroy() {
this.destroy$.next();
}
}
Comments
-
Sibiraj almost 2 years
I am new to angular2 and I have been trying to create a resizable div (vertically). but I am unable to achieve that. What I have tried using a directive
and this is my directive
import { Directive, HostListener, ElementRef, Input } from '@angular/core'; @Directive({ selector: '[appNgxResizer]' }) export class NgxResizerDirective { constructor(private el: ElementRef) { } @HostListener('mousemove', ['$event']) resize(e) { this.el.nativeElement.parentNode.style.height = (e.clientY - this.el.nativeElement.parentNode.offsetTop) + 'px'; event.preventDefault(); } @HostListener('mouseup', ['$event']) stopResize(e) { event.preventDefault(); } }
Here is the stackblitz for what I have tried https://stackblitz.com/edit/angular-text-resizable-q6ddyy
I want to click grab to resize the div. Something like this https://jsfiddle.net/zv2ep6eo/.
Thanks.