簡體   English   中英

在 AngularJS 指令中編譯模板之前評估嵌入的文本

[英]Evaluate transcluded text before compiling template in AngularJS directive

我正在 AngularJS 中處理“collapseText”指令。 它的功能是顯示最多 "maxLength" 個字符和一個 "Read More" 選項,如果文本太大,它將加載其余的文本。

我希望我的指令能夠嵌入文本,包括表達式。

理想情況下,我希望它看起來像這樣:

<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 語句更改為“overflow === true”、“readMore === false”和“readMore === true”來修復未重新評估的 ng-if 語句。 我仍然希望澄清一下為什么它不能簡單地與 . 關於嵌入文本評估的主要問題仍然存在。
  2. PENDING :{{foo}} 被打印為“{{foo}}”而不是“foo 包含的文本”。

在此先感謝您的幫助!

在指令的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>

模板.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"
    }
});

正如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 表達式完美配合。

使用.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>

模板.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"
    }
});

在實現類似的東西時,我將不勝感激關於代碼和“最佳實踐”的進一步反饋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM