beNerd
beNerd

Reputation: 3374

getting two way binding in a directive without isolated scope

I have a directive like this:

angular.module('somemodule').directive('tooltipText', function ($timeout, $compile, $document, config, $filter) {
  return {
    restrict: 'A',

    scope:{
       tooltip: '='
     },
    link: function (scope, element, attrs) {
      let tipHtml = `<div ng-class="tipClass">
                      <div class="header">
                        <span>{{ title }}</span>
                      </div>
                      <div class="body">
                        <br ng-show="showDescription && description"/>
                        <span ng-show="showDescription">{{description}}</span>
                      </div>
                      <div class="tooltip-arrow"></div> 
                    </div>`;

    scope.$watch('tooltip',(changedAttr) => {

    if (element) {
      let elm = angular.element(element)[0];
      if (elm.getAttribute('tooltip-show')) {

        if (changedAttr && elm.offsetParent != null) {
          showtooltip(element);
          elm.scrollIntoView();

        }
      } else {
        element.bind('mouseover', showtooltip);
      }
    }
  });
 showtooltip = (elem)=>{
 //do some tooltip logic and then after a timeout of say, 5secs, I do

   scope$.tooltip = false;
   //i.e i remove the tooltip and set the attribute
   // value back so that next time change can be 
   // watched by this directive. Basically it's a tooltip
   //that should last for around 5 seconds and when
   //setting the attribute on element again to true,
   // it should reappear.
   }
});

Now the problem is that, with this isolated scope, it is conflicting with other directives. What's the best approach around this?

Upvotes: 0

Views: 53

Answers (1)

georgeawg
georgeawg

Reputation: 48968

To avoid using isolate scope, simply watch the attribute:

scope: false,  ̶{̶
          ̶t̶o̶o̶l̶t̶i̶p̶:̶ ̶'̶=̶'̶
      ̶}̶,̶
link: function (scope, element, attrs) {
    let tipHtml = `<div ng-class="tipClass">
                  <div class="header">
                    <span>{{ title }}</span>
                  </div>
                  <div class="body">
                    <br ng-show="showDescription && description"/>
                    <span ng-show="showDescription">{{description}}</span>
                  </div>
                  <div class="tooltip-arrow"></div> 
                </div>`;

    ̶s̶c̶o̶p̶e̶.̶$̶w̶a̶t̶c̶h̶(̶'̶t̶o̶o̶l̶t̶i̶p̶'̶,̶(̶c̶h̶a̶n̶g̶e̶d̶A̶t̶t̶r̶)̶ ̶=̶>̶ ̶{̶
    scope.$watch(attrs.tooltip,(changedAttr) => {

On each digest cycle the watcher will evaluate the attribute as an Angular Expression and invoke the listening function with any new value.

For more information, see AngularJS $watch API Reference.


that's fine. but i need to push the changes back to parent attribute. How will that work? so after say 10 seconds i need to do something like attrs.tooltip = false; And this is not a static value set like attrs.$set does. I need the actual binding..because otherwise after those 10 seconds the value would still be true and the tooltip won't be triggered again

Use the $parse service

function postLink(scope, elem, attrs) {
    var getter = $parse(attrs.tooltip);
    var setter = getter.assign;
    setter(scope,false);
};

If the attribute is assignable, this will set it to false on the scope context.

Be aware that two-way binding makes it difficult to reason about what data is changed, and when. Only the component that owns the data should modify it. Inputs should be one-way and outputs should be implemented as callbacks.

Upvotes: 1

Related Questions