angular 2+ vertically resize div
Asked Answered
T

5

7

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.

Tiebold answered 26/9, 2017 at 8:5 Comment(0)
C
10

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.

Stackblitz example

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>
Campania answered 26/9, 2017 at 11:10 Comment(6)
using directives I was unable to get the elementRef inside the function. It gave me a headache.Tiebold
thank you. this is great.let this question be open for more answers. If not I will accept this answer.Tiebold
check the directive I made using your solution. stackblitz.com/edit/angular-text-resizable-v7pqzv. Correct me if I have done anything wrong?Tiebold
nope. do you mean the question. or the directive I made with your answerTiebold
Sorry i forgot. Accepted now.Tiebold
Thank you for this ! I made something based on your concept: stackblitz.com/edit/two-div-with-resizerFootplate
T
4

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>
Tiebold answered 26/9, 2017 at 12:42 Comment(0)
P
2

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)

Passible answered 26/9, 2017 at 8:41 Comment(6)
thanks. I was looking something like this jsfiddle.net/zv2ep6eoTiebold
Fixed your stackbiltz to make this work. See this: angular-text-resizable-q6ddyy.stackblitz.ioPassible
But resizing down is also not working in the fiddle you added.Passible
Mistakenly gave you the same link as yours. Let me update answer with the changes I made to your directive.Passible
@SurenderKherwa, Hello...can you please kindly share the code for this angular-text-resizable-c8hmat.stackblitz.io . I am looking for something exactly like this. But unfortunately I cannot see your code in Stackblitz editor. Please kindly help me. CheersLawabiding
@SurenderKherwa ok, Got it man..this should be the accepted answer. Awesome solution. CheersLawabiding
P
1

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>
Philibeg answered 19/3, 2019 at 22:9 Comment(0)
A
1

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

}
Accordance answered 8/6, 2020 at 8:18 Comment(1)
Is it possible to add max and min element width in this directive?Pruitt

© 2022 - 2024 — McMap. All rights reserved.