Arak Tai'Roth
Arak Tai'Roth

Reputation: 408

Access value from controller in directive

I'll put my code in and then I'll explain what I'm trying to accomplish.

My directive:

chat.directive('autoScroll', function($timeout) {
    return {
        restrict: 'A',
        scope: {
            totalHeight: '='
        },
        link: function(scope, element, attrs) {
            var parent = element.parent('div.chat-text');
            $timeout(function() {
                scope.totalHeight += element.height();
            }, 0);
            parent.scrollTop(scope.totalHeight);
        }
    };
});

And where I'm calling the directive:

<div id={{$index}} class='chat-window' ng-class='{active: chat.selected == true}' ng-click='selectChat($index)' ng-repeat='chat in chatWindows track by $index' resize-chat draggable='true' ondragstart='dragStart(event)'>
    <span class='title'>{{chat.name}}<i class='fa fa-times pull-right click' ng-click='remove(chatWindows, $index)'></i></span>
    <div class='chat-text'>
        <div class='message' ng-repeat='message in chat.messages' auto-scroll totalHeight='chat.totalHeight'>
            <span class='name'>{{message.user}}</span>
            <span class='time'>{{message.time}}</span>
            <p>{{message.message}}</p>
        </div>
    </div>
</div>

The directive is getting called on the div.message. The chatWindows object and an idea of what the object I'm trying to access looks like:

$scope.chatWindows = [];
$scope.chatWindows.push({name: name, selected: true, totalHeight: 0, messages:[]});

So, now what I'm trying to accomplish. As messages are added to the chatWindow object, I want to get the height of that message, add it to the total height of the chatWindow object, and then move the scrollbar to that position.

Currently what's happening is I'm getting an error "Expression 'undefined' used with directive 'autoScroll' is non-assignable!".

I'm brand new to angular, and currently trying to wrap my head around directives, but having a really hard time finding out how to solve this issue.

Upvotes: 0

Views: 99

Answers (2)

dave walker
dave walker

Reputation: 3108

By using the scope property in your directive definition object you have created an isolated scope for the directive. Anything you want from the parent scope must be passed down through attributes in the html.

You probably want to use interpolation with {{chat.totalHeight}}

I can't see why the interpolated value is not getting into your isolated scope, but just in case the ngRepeat directive is giving you grief, try putting your directive inside the repeated element.

<div id={{$index}} class='chat-window' ng-class='{active: chat.selected == true}' ng-click='selectChat($index)' ng-repeat='chat in chatWindows track by $index' resize-chat draggable='true' ondragstart='dragStart(event)'>
<span class='title'>{{chat.name}}<i class='fa fa-times pull-right click' ng-click='remove(chatWindows, $index)'></i></span>
<div class='chat-text'>
    <div class='message' ng-repeat='message in chat.messages'>
      <div auto-scroll totalHeight='{{chat.totalHeight}}'
        <span class='name'>{{message.user}}</span>
        <span class='time'>{{message.time}}</span>
        <p>{{message.message}}</p>
      </div>
    </div>
</div>

or to ditch the isolated scope (don't do both of these):

chat.directive('autoScroll', function($timeout) {
return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        var parent = element.parent('div.chat-text');
        $timeout(function() {
            scope.chat.totalHeight += element.height();
        }, 0);
        parent.scrollTop(scope.chat.totalHeight);
    }
};
});

To answer the question regarding when we would want to isolate the scope, you will often want your directives to be scope agnostic so you can use the same directive anywhere in the application regardless of its parent scope. This is, in fact, usually what you will want to do. For example this directive (without an isolated scope) can only be used when the parent scope has a chat.totalHeight property. Not very re-usable.

Upvotes: 1

Sten Muchow
Sten Muchow

Reputation: 6711

There could be a potential of totalHeight not be defined to begin with - some asynchronous issues I have faced in the past. You can try to add a watcher to be sure it is being defined properly.

chat.directive('autoScroll', function($timeout) {
    return {
        restrict: 'A',
        scope: {
            totalHeight: '='
        },
        link: function(scope, element, attrs) {
            var parent = element.parent('div.chat-text');
            scope.$watch('totalHeight', function() {
               console.log(scope.totalHeight); //Not necessary but interesting to see what is going on
               if(!!scope.totalHeight) {              
                 scope.totalHeight += element.height();
                 parent.scrollTop(scope.totalHeight);
               }
        }
    };
});

Maybe this solves your problem maybe not, but I know I have come across similar issues in the past - I am not completely happy with the way this code works, feel a bit hacky, but unless someone else know a way to get around this issue of scope.$watch this stands for me...

Upvotes: 0

Related Questions