47

My site will have multiple sections, each of which I intend to be resizable. To accomplish this I've made a "resizable" directive, e.g.:

<div class="workspace" resize="full" ng-style="resizeStyle()">
<div class="leftcol" resize="left" ng-style="resizeStyle()">

With a directive that looks something like:

lwpApp.directive('resize', function ($window) {
    return {
        scope: {},

        link: function (scope, element, attrs) {
            scope.getWinDim = function () {
                return {
                    'height': window.height(),
                    'width': window.width()
                };
            };

            // Get window dimensions when they change and return new element dimensions
            // based on attribute
            scope.$watch(scope.getWinDim, function (newValue, oldValue) {
                scope.resizeStyle = function () {
                    switch (attrs.resize) {
                    case 'full':
                        return {
                            'height': newValue.height,
                            'width': (newValue.width - dashboardwidth)
                        };

                    case 'left':
                        return {
                            'height': newValue.height,
                            'width': (newValue.width - dashboardwidth - rightcolwidth)
                        };

                    etc...
                };
            }, true);

            //apply size change on window resize
            window.bind('resize', function () {
                scope.$apply(scope.resizeStyle);
            });
        }
    };
});

As you can see, this only resizes each div on window resize, and each directive has an isolate scope. This works fine for what it's built for, but ultimately I would like to make a subset of the divs resizable via a draggable bar. For instance

div1     div2
----------------
|     ||       |
|     ||       |
|     ||       |
|     ||       |
----------------
    draggable bar in middle

On the the draggable bar's movement (in the horizontal direction), I would need to access both div1, div2's width presumably via the scope of a parent controller(?). My questions are:

  1. Is this the "correct" way to go about making resizable divs in angular? In particular, when the size of one div affects another?

  2. I personally feel like the answer to (1) is "No, I am not doing it correctly because I cannot communicate between directives when each has an isolate scope." If this is true, how can I account for both window and draggable resizing between divs?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
jayflo
  • 1,105
  • 1
  • 12
  • 21
  • I don't have a clear answer for you but I would check out things like the BootstrapUI Accordion or similar where there is a main directive that uses some other directives and "communicates" the appropriate information between them to open and close the appropriate sections. I believe you'll be using require property of the directive definition object, see the long version here http://docs.angularjs.org/guide/directive – shaunhusain Aug 21 '13 at 22:21
  • Thanks! I think you might be right with the require. That will give me access to another controller... – jayflo Aug 22 '13 at 15:25
  • I've just been trying this out. I had a go with jQuery-Splitter as jayflo suggested but I struggled with this and also got the impression the code is unsupported. I've tried again using [JQueryUI resizable](http://plugins.jquery.com/ui.resizable/) and that is working fine. Next step is to integrate with Angular, and I'm going to use [AngularJS-JQueryUI](https://github.com/wlepinski/angularjs-jqueryui) – paj28 Feb 17 '14 at 23:31

4 Answers4

99

This question is old, but for anybody looking for a solution, I built a simple directive to handle this, for vertical and horizontal resizers.

Take a look at the Plunker

enter image description here

angular.module('mc.resizer', []).directive('resizer', function($document) {

    return function($scope, $element, $attrs) {

        $element.on('mousedown', function(event) {
            event.preventDefault();

            $document.on('mousemove', mousemove);
            $document.on('mouseup', mouseup);
        });

        function mousemove(event) {

            if ($attrs.resizer == 'vertical') {
                // Handle vertical resizer
                var x = event.pageX;

                if ($attrs.resizerMax && x > $attrs.resizerMax) {
                    x = parseInt($attrs.resizerMax);
                }

                $element.css({
                    left: x + 'px'
                });

                $($attrs.resizerLeft).css({
                    width: x + 'px'
                });
                $($attrs.resizerRight).css({
                    left: (x + parseInt($attrs.resizerWidth)) + 'px'
                });

            } else {
                // Handle horizontal resizer
                var y = window.innerHeight - event.pageY;

                $element.css({
                    bottom: y + 'px'
                });

                $($attrs.resizerTop).css({
                    bottom: (y + parseInt($attrs.resizerHeight)) + 'px'
                });
                $($attrs.resizerBottom).css({
                    height: y + 'px'
                });
            }
        }

        function mouseup() {
            $document.unbind('mousemove', mousemove);
            $document.unbind('mouseup', mouseup);
        }
    };
});
Mario Campa
  • 4,092
  • 1
  • 26
  • 25
21

I know I'm a bit late to the party, but I found this and needed my own solution. If you're looking for a directive that works with flexbox, and doesn't use jquery. I threw one together here:

http://codepen.io/Reklino/full/raRaXq/

Just declare which directions you want the element to be resizable from, and whether or not you're using flexbox (defaults to false).

<section resizable r-directions="['right', 'bottom']" r-flex="true">
klinore
  • 2,589
  • 4
  • 19
  • 21
  • 2
    Very helpful! Here's a super simple plunker (using angular-resizable) for those who want to get it working real quick. Just look at `index.html` https://plnkr.co/edit/ww3axpDkBTJuGX1gfIlA?p=preview – Derek Soike Jun 29 '16 at 00:15
3

For the needs of my project i added support of minimum values, so that panels can keep some width or height - (here is the gist) - Github

Also, i created Github repo, where i added support for panels being located right of main page axis and support of minimum/maximum values. It's in example stage now, but i'm willing to turn it into a full-weight Angular directive

n3u3w3lt
  • 56
  • 6
0

This does not completely answer the question, but changing scope: true solved the isolate scope problem. In particular, in my html I have:

<div ng-controller="WorkspaceCtrl">
  <div class="workspace" resize="full" ng-style="resizeStyle()">
    <div class="leftcol" resize="left" ng-style="resizeStyle()">
      <ul class="filelist">
        <li ng-repeat="file in files" id={{file.id}} ng-bind=file.name></li>
      </ul>
      <div contenteditable="true" ng-model="content" resize="editor" ng-style="resizeStyle()">
        Talk to me
      </div>
    </div>
</div>

and ng-repeat="file in files" still has access to the array $scope.files defined in the controller WorkspaceCtrl. So scope: {} cuts off the scope of the directive from the scope of the parent controller, whereas scope: true simply creates a new scope for each instance of the directive AND each instance of the directive, along with its children, retains access to the parent scope.

I have not yet implemented the draggable bar which resizes these divs, but will report back when I do so.

jayflo
  • 1,105
  • 1
  • 12
  • 21
  • Any luck implementing this yet? – treeface Sep 24 '13 at 16:05
  • No, sorry. It's simple to make divs resize on window resize. I've become distracted with other things and haven't tried to make the divs themselves resizable. However, I'm sure that it would be accomplished by using draggable "shims" which lie inbetween the divs. Also, make sure you don't isolate the scope for any directives you wish to resize this way. – jayflo Sep 25 '13 at 20:59
  • Since I asked this yesterday, I found a fairly decent solution to the problem. Check out this forked version of jQuery Splitter: https://github.com/e1ven/jQuery-Splitter. To get it to work with jQuery 1.9+, you need to reinstate jQuery.browser (I use [this code](http://paste.laravel.com/UDz)). Hook that up with a linking function and you should be good to go. – treeface Sep 25 '13 at 21:55