How can I get ng-click to function with ng-repeat and ng-bind-html?

331 views Asked by At

I am trying to get a ng-click directive to function within an ng-repeat and ng-bind-html. The ng-click code is added to a string of html from data pulled from the server (hence the ng-bind-html). The setup has a controller, a base template that is put onto the page with Drupal, and a partial that is loaded via the template from Drupal.

The controller looks like this at the moment:

var guideListController = angular.module('app')
  .controller('guideListController', [
    '$scope',
    '$sce',
    '$compile',
    'ViewService',
    'ToolKit',
  function($scope, $sce, $compile, ViewService, ToolKit) {

  // Storage for which rows need more/less links
  this.rowIndex = [];

  this.showFull = false;
  this.showFullClick = function() {
    alert('Showfull');
  };

  this.trustIntro = function(code) {
    return $sce.trustAsHtml(code);
  };

  // Fetch the guide list view from services
  var data = ViewService.get({view_endpoint:'guide-service', view_path: 'guide-list'}, function(data) {
    //console.log(data);

    // Update/process results 
    for (var row in data.results) {

      // Create short intro w/ truncate.js
      data.results[row].Intro_short = $sce.trustAsHtml($scope.guideList.getShortIntro(data.results[row], row));
      //data.results[row].Intro_short = $scope.guideList.getShortIntro(data.results[row], row);

      // Update intro
      data.results[row].Introduction = $sce.trustAsHtml($scope.guideList.updateIntro(data.results[row], row));
      //data.results[row].Introduction = $scope.guideList.updateIntro(data.results[row], row);

    }
    $scope.guideList.guides = data.results;
  });


  // Add a read less anchor tag at the end of the main intro
  this.updateIntro = function(row, row_index) {
    var intro = row['Introduction'].trim();

    if ($scope.guideList.rowIndex[row_index]) { // only apply Less link if needed
      var index = intro.length - 1;
      var tag = [];
      if (intro.charAt(index) === '>') { // we have a tag at the end
        index--;
        do {
          tag.push(intro.charAt(index));
          index--;
        } while (intro.charAt(index) != '/'); // the closing tag
        index--; // we move the index one more for the "<"
        tag.reverse(); // Reverse
        tag = tag.join('');
      }

      var inserts = ['div', 'p']; // we insert the Less link here.
      if (jQuery.inArray(tag, inserts) >= 0) { // insert into the tag
        intro = intro.substr(0, index) + ' <a class="less" ng-click="$parent.guideList.showFull = false">Less</a>' + intro.substr(index);
      }
      else { // insert at the end of the html
        intro = intro + '<a class="less" ng-click="this.showFull = false">Less</a>';
      }
    }

    return intro; 
  };


  // Truncate the long intro into a shorter length blurb
  this.getShortIntro = function(row, row_index) {
    // Truncate if necc.
    var short_intro = jQuery.truncate(row['Introduction'], {
      length: 250,
      words: true,
      ellipsis: '\u2026 <a class="more moreish" attr-ng-click="guideList.showFullClick()">Read&nbsp;on</a>'
    });

    var more = jQuery('.more', short_intro); // select more link

    if (more.length) { // do we have a more link
      $scope.guideList.rowIndex[row_index] = true;
    }
    else { // no more link
      $scope.guideList.rowIndex[row_index] = false;
    }
    $compile(short_intro)($scope);
    return short_intro;
  };
}]);

As you can see in the ViewService.get() call, data is fetched and then processed. The processing simply involves putting a link at the end of the "Intro" field that is intended to be clickable.

For a while I was having a tough time to even get the ng-click directive to even show (it was being filtered out w/out $sce.trustAsHtml). Now it is there but clicking it has no effect.

The main template (from Drupal) currently looks like:

<div class="guide-listing" ng-controller="guideListController as guideList">
  <a ng-click="guideList.showFullClick()">Click me</a>
  <div class="guide-teaser" 
       ng-repeat="guide in guideList.guides"
       ng-include src="'/sites/all/themes/ngTheme/ngApp/partials/guide_teaser.html'">
       <!-- See guide_teaser.html partial for guide teasers -->
  </div>
</div>

The ng-click as placed in the Drupal template above works as expected.

And for the partial that is used in the ng-repeat, it looks like so:

<div ng-controller="guideListController as guideList">
  <h2 class="guide-teaser-title"><a href="{{guide.path}}">{{guide.node_title}}</a></h2>
  <div class="guide-teaser-intro" ng-bind-html="guide.Introduction" ng-show="guide.showFull">
    {{guide.Introduction}}
  </div>
  <div class="guide-teaser-intro-short" ng-bind-html="guide.Intro_short" ng-show="!guide.showFull">
    {{guide.Intro_short}}
  </div>
</div>

So far I have only been working on getting the ng-click to work on the short_intro and have had no success so far. Any ideas as to what I am doing wrong would be greatly appreciated!

1

There are 1 answers

0
Derek Webb On

Ok, So I did get some traction! I used the ngHtmlCompile (http://ngmodules.org/modules/ng-html-compile) directive that was created by https://github.com/francisbouvier (thanks!)

The issue was that the new (dynamic) html wasn't being compiled.

At first it didn't work. I had two issues that prevented it from firing:

A: I stopped using $sce.trustAsHtml. Using this in conjunction with the directive caused the content to disappear!

B: The other issue was one of scope. After I changed the directive such that transclude was set to false it worked just fine!