У меня есть div, в котором у меня есть директива, которая связывает содержимое HTML и компилирует его (своего рода директива ng-bing-html, но также компилирует html, чтобы разрешить вставку пользовательских директив). HTML-код выглядит так:

<div ng-repeat="text in texts">
     <div class="content-display" 
          bind-html-compile="text | filterThatOutputsHTMLCodeWithCustomDirectives | nl2br">
     </div>
</div>

Проблема в том, что мне нужно отображать только ограниченную часть каждого из content-display элементов div и иметь кнопку «читать дальше ...», которая расширит соответствующий элемент div до его полного размера. Но я НЕ МОГУ обрезать текст, связанный в div, поскольку он не только текст, но может содержать теги / директивы HTML.

Я нашел этот код JQuery, который выполняет то, что я хочу визуально : https://stackoverflow.com/a/ 7590517/2459955 (JSFiddle здесь: http://jsfiddle.net/rlemon/g8c8A/ 6 /)

Проблема в том, что он не совместим с Angular и является чистым JQuery. И поскольку мой div, в котором я связываю содержимое HTML, находится внутри ng-repeat ... это решение не будет работать, когда массив texts обновляется асинхронно.

Видите ли вы способ иметь такое же поведение, как в ответе, связанном ранее, но быть более «угловым» и применять его автоматически к каждому из content-display элементов, добавленных ng-repeat?

0
Hexalyse 14 Дек 2015 в 14:15

5 ответов

Лучший ответ

Наконец, я использовал подход, приведенный в этом ответе, с небольшим изменением: https://stackoverflow.com/a/7590517/2459955

В самом деле, поскольку у меня ng-repeat добавлено больше элементов div в DOM, функция $elem.each() не сработает для этих дополнительных элементов div. Решение состоит в том, чтобы использовать плагин JQuery под названием jquery.initialize.

Этот плагин предоставляет функцию $elem.initialize(), которая имеет тот же синтаксис, что и $elem.each(), но initialize () будет вызывать обратный вызов снова для новых элементов, соответствующих предоставленному селектору, автоматически, когда они будут добавлены в DOM. Он использует MutationObserver .

Окончательный код выглядит следующим образом. У меня есть некоторый код JQuery в моей записи module.run() (запускается один раз при инициализации модуля):

var slideHeight = 400;
$(".content-collapse").initialize(function() {
  var $this = $(this);
  var $wrap = $this.children(".content-display");
  var defHeight = $wrap.height();
  if (defHeight >= slideHeight) {
    var $readMore = $this.find(".read-more");
    var $gradientContainer = $this.find(".gradient-container");
    $gradientContainer.append('<div class="gradient"></div>');
    $wrap.css("height", slideHeight + "px");
    $readMore.append("<a href='#'>Read more</a>");
    $readMore.children("a").bind("click", function(event) {
      var curHeight = $wrap.height();
      if (curHeight == slideHeight) {
        $wrap.animate({
          height: defHeight
        }, "normal");
        $(this).text("Read less");
        $gradientContainer.children(".gradient").fadeOut();
      } else {
        $wrap.animate({
          height: slideHeight
        }, "normal");
        $(this).text("Read more");
        $gradientContainer.children(".gradient").fadeIn();
      }
      return false;
    });
  }
});

И соответствующий HTML (убран для демонстрации):

 <div class="content-collapse" ng-repeat="text in texts">
    <div class="content-display" bind-html-compile="::text"></div>
    <div class="gradient-container"></div>
    <div class="read-more"></div>
 </div>

Это решение обеспечивает плавную анимацию развертывания / свертывания, которая отлично работает без какого-либо взлома CSS, добавляет кнопку «Читать далее» только к ответам, размер которых превышает желаемый предел размера, и работает, даже если массив texts изменяется асинхронным Запросы.

0
Community 23 Май 2017 в 12:33

Одним чистым способом было бы использовать класс для усеченного div, и удалить его, чтобы отобразить весь текст:

Угловая область:

$scope.truncated = []; // make new array containing the state of the div (truncated or not)
for(var i; i < texts.length -1; i++){
    $scope.truncated.push(0); // fill it with 0 (false by default)
}
$scope.textTruncate = function(index) {
    $scope.truncated[index] = !$scope.truncated[index]; // toggle this value
}

Угловой вид:

<div ng-repeat="text in texts" ng-class="{truncated: truncated[$index]}">
    <div class="content-display" 
          bind-html-compile="text | filterThatOutputsHTMLCodeWithCustomDirectives | nl2br">
     </div>
    <button ng-click="textTruncate($index)" >Read more</button>
</div>

CSS:

.content-display {
    max-height: 1000px; /* should be your max text height */
    overflow: hidden;
    transition: max-height .3s ease;
}
.truncated .content-display {
    max-height: 100px; /* or whatever max height you need */

}

Это то, что приходит мне в голову, я не уверен, что это самый эффективный способ.

1
enguerranws 14 Дек 2015 в 13:34

У меня была похожая проблема. Я должен был реализовать это для таблицы данных. Я нашел следующую директиву, и она работала гладко в соответствии с требованиями:

Ui Framework - угловой JS

В HTML

  <tr data-ng-repeat="proj in errors">
        <td dd-text-collapse dd-text-collapse-max-length="40" 
        dd-text-collapse-text="{{proj.description}}"></td> 

В JavaScript: -

    app.directive('ddTextCollapse', ['$compile', function($compile) {


      return {
          restrict: 'A',
          scope: true,
          link: function(scope, element, attrs) {


              /* start collapsed */
              scope.collapsed = false;


              /* create the function to toggle the collapse */
              scope.toggle = function() {
                  scope.collapsed = !scope.collapsed;
              };


              /* wait for changes on the text */
              attrs.$observe('ddTextCollapseText', function(text) {


                  /* get the length from the attributes */
                  var maxLength = scope.$eval(attrs.ddTextCollapseMaxLength);


                  if (text.length > maxLength) {
                      /* split the text in two parts, the first always showing */
                      var firstPart = String(text).substring(0, maxLength);
                      var secondPart = String(text).substring(maxLength, text.length);


                      /* create some new html elements to hold the separate info */
                      var firstSpan = $compile('<span>' + firstPart + '</span>')(scope);
                      var secondSpan = $compile('<span ng-if="collapsed">' + secondPart + '</span>')(scope);
                      var moreIndicatorSpan = $compile('<a ng-if="!collapsed">... </a>')(scope);
                      var lineBreak = $compile('<br ng-if="collapsed">')(scope);
                      var toggleButton = $compile('<a class="collapse-text-toggle" ng-click="toggle()">{{collapsed ? "(less)" : "(more)"}}</a>')(scope);


                      /* remove the current contents of the element
                       and add the new ones we created */
                      element.empty();
                      element.append(firstSpan);
                      element.append(secondSpan);
                      element.append(moreIndicatorSpan);
                      element.append(lineBreak);
                      element.append(toggleButton);
                  }
                  else {
                      element.empty();
                      element.append(text);
                  }
              });
          }
      };
       }]);
0
Mayank Parnami 11 Май 2018 в 08:09

Попробуйте использовать <p data-dd-collapse-text="100">{{veryLongText}}</p> внутри ng-repeat

Документация здесь

0
Tomaltach 14 Дек 2015 в 11:25

Попробуйте использовать CSS-подход, подобный описанному здесь: https://css-tricks.com / текст - нарастающее чтение более /

CSS:

.sidebar-box {
  max-height: 120px;
  position: relative;
  overflow: hidden;
}
.sidebar-box .read-more { 
  position: absolute; 
  bottom: 0; 
  left: 0;
  width: 100%; 
  text-align: center; 
  margin: 0; padding: 30px 0; 

  /* "transparent" only works here because == rgba(0,0,0,0) */
  background-image: linear-gradient(to bottom, transparent, black);
}

Вместо того чтобы использовать jQuery для «раскрытия» read more, вы можете создать директиву AngularJS для кнопки read more.

Директива (не проверена):

angular.module('myApp')
    .directive('readMore', readMoreDirective);

function readMoreDirective() {
    return function(scope, iElement) {
        scope.$on('click', function() {
            var totalHeight = 0;
            var parentElement = iElement.parent();
            var grandparentElement = parentElement.parent();
            var parentSiblings = grandparentElement.find("p:not('.read-more')");

            // measure how tall inside should be by adding together heights
            // of all inside paragraphs (except read-more paragraph)
            angular.forEach(parentSiblings, function(ps) {
                totalHeight += ps.outerHeight();
            });

            grandparentElement.css({
                // Set height to prevent instant jumpdown when max height is removed
                height: grandparentElement.height(),
                'max-height': 9999
            })
            .animate({
                height: totalHeight
            });
        });
    };
}
1
Shaun Scovil 14 Дек 2015 в 12:57