在AngularJS指令中编译模板之前评估已转换的文本

时间:2016-04-03 05:54:39

标签: javascript angularjs

我正在研究" collapseText" AngularJS中的指令。它的功能是显示最大值" maxLength"字符和"阅读更多"如果文本太大,则会加载其余文本的选项。

我希望我的指令能够转换文本,包括表达式。

理想情况下,我希望它看起来像这样:

<collapse-text max-length="10">This text will be collapsed</collapse-text>
<collapse-text max-length="10">{{foo}}</collapse-text>

我使用的模板是:

<span>{{lessText}}</span>
<span ng-if="overflow">
    <span ng-if="!readMore" style="cursor:pointer" ng-click="toggleReadMore()">...(read more)</span>
    <span ng-if="readMore">{{moreText}}</span>
</span>

我的指示如下:

'use strict'
angular.module('myModule')

.directive('collapseText', function($window){
    return{
        restrict: 'E',
        scope: true,
        transclude: true,
        controller : function($scope){
            $scope.toggleReadMore = function(){
                $scope.readMore = true;
            }
        },
        link: function(scope, element, attrs, ctrl, transclude){
            scope.maxLength = attrs.maxLength;
/* 1. Evaluate transcluded element */
/* 2. Check transcluded element's length */
/* 3. Set lessText, moreText, readMore and overflow */
/* 4. Evaluate this directive's template */
            console.log(transclude(scope.$parent, function(compiled){
                scope.lessText = compiled.text().substring(0, scope.maxLength);
                scope.moreText = compiled.text().substring(0, scope.maxLength);
                scope.readMore = false;
                scope.overflow = scope.moreText ? true : false;
                return compiled.text();
            }).text());
        },
        templateUrl: "templates/collapse-text-template.html"
    }
});

通过步骤1-4的正确方法是什么? 我看到的两个症状是:

  1. 已解决:在溢出和readMore变量更新后,不会重新评估ng-if语句,因此,这些文本字段永远不会出现在DOM中。
    • 我已经修复了ng-if语句没有被重新评估,方法是将它们更改为&#34; overflow === true&#34;,&#34; readMore === false&#34;和&#34; readMore === true&#34 ;,分别。我仍然很清楚为什么它不能简单地使用。关于被盗文本评估的主要问题仍然存在。
  2. PENDING :{{foo}}打印为&#34; {{foo}}&#34;而不是&#34; foo包含的文本&#34;。
  3. 提前感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

在指令的link()功能中,您必须等到{{foo}}进行评估后才能使用。这可以通过使用$timeout()在浏览器的事件循环中安排新任务来完成。我不确定它是否是最干净的解决方案,但至少它是否有效。

以下是$timeout()的代码和一些小改进:

<div ng-app="myModule" ng-controller="MyController">
    <collapse-text max-length="10">This text will be collapsed</collapse-text>
    <collapse-text max-length="10">{{foo}}</collapse-text>
</div>

<强> template.html

<span ng-if="!readMore">{{lessText}}</span>
<span ng-if="overflow">
    <span ng-if="!readMore && overflow" style="cursor: pointer;" ng-click="toggleReadMore()">...(read more)</span>
    <span ng-if="readMore">{{moreText}}</span>
</span>

<强>脚本

angular.module('myModule', []).controller('MyController', function($scope){
    $scope.foo = 'This text will also be collapsed';
});

angular.module('myModule').directive('collapseText', function($timeout){
    return {
        restrict: 'E',
        scope: true,
        transclude: true,
        controller: function($scope){
            $scope.toggleReadMore = function(){
                $scope.readMore = true;
            };
        },
        link: function(scope, element, attrs, ctrl, transclude){
            var maxLength = +attrs.maxLength;
            var compiled = transclude(scope.$parent);

            $timeout(function(){
                scope.lessText = compiled.text().substring(0, maxLength);
                scope.moreText = compiled.text();
                scope.readMore = false;
                scope.overflow = scope.moreText.length > maxLength;
            });
        },
        templateUrl: "template.html"
    }
});


请注意,此实现不会对$scope.foo的更新做出反应(即指令不会看到更新并重新呈现)。如果您需要这个,我建议您将内容传递给属性中的指令并实现观察者而不是使用翻译。例如:

angular.module('myModule').directive('collapseText', function(){
    return {
        restrict: 'E',
        scope: {
            myContent: '=',
            // ...
        },
        link: function(scope){
            scope.$watch('myContent', function(newValue){
                if (newValue !== undefined) {
                    doSomethingWith(newValue);
                }
            });
        },
        templateUrl: "template.html"
    }
});

答案 1 :(得分:0)

正如Aletheios建议的那样,我想出了一种方法,即通过在转换文本上实现监视和手动转换函数,该函数只是将原始输入存储在本地范围内。 它的实现方式,它将在“lessText”字段中显示原始输入,稍后将由执行其转换的父级处理。此转换将在collapseText指令中触发监视。

我可以想到可能会出现的一些问题,虽然我不完全确定它们,因为我对AngularJS很新:

  • 如果文本长于“maxLength”显示的文本被删除且需要进一步处理,它会起作用吗?例如:

    <collapse-text max-length="10" {{myLongVariableName.anotherLongVariableName}}</collapse-text>
    
  • 此代码不允许带有任何标记的文本。例如:

    <collapse-text max-length="20"><b>I am bold</b></collapse-text>
    
  • 即使它允许带有标记的文本,当分割为lessText和moreText时,标记可能会被破坏。

解决方案

初始测试指出它与纯文本和AngularJS表达式完美配合。

use.html

这里需要注意的一点是,我打算在另一个指令中使用此代码,因此{{parent.foo}}也应该有用。

<div ng-app="myModule" ng-controller="MyController">
    <collapse-text max-length="10">This text will be collapsed</collapse-text>
    <collapse-text max-length="10">{{foo}}</collapse-text>
</div>

template.html

<span ng-if="readMore === false">{{lessText}}</span>
<span ng-if="overflow === true">
    <span ng-if="readMore === false" style="cursor:pointer" ng-click="toggleReadMore()">...(read more)</span>
    <span ng-if="readMore === true">{{moreText}}</span>
</span>

脚本

angular.module("myModule")
.controller('MyController', function($scope){
    $scope.foo = 'This text will also be collapsed';
})
.directive('collapseText', function($window){
    return{
        restrict: 'E',
        scope: true,
        transclude: true,
        controller : function($scope){
            $scope.readMore = false;
            $scope.toggleReadMore = function(){
                $scope.readMore = true;
            }

            $scope.$watch(
                            function(){
                                return $scope.transcluded;
                            },
                            function(newValue){
                                if($scope.transcluded){
                                    $scope.lessText = $scope.transcluded.text().substring(0, $scope.maxLength);
                                    $scope.moreText = $scope.transcluded.text();
                                    $scope.readMore = false || $scope.readMore; //If it was arleady true, do not collapse it again.
                                    $scope.overflow = $scope.moreText ? true : false;
                                }
                            }
                        );
        },
        link: function(scope, element, attrs, ctrl, transclude){
            scope.maxLength = attrs.maxLength;
            transclude(scope.$parent, function(transcluded){
                scope.transcluded = transcluded;
            });
        },
        templateUrl: "template.html"
    }
});

在实施类似的内容时,我非常感谢有关代码和“最佳实践”的进一步反馈。