【问题标题】:How to re-render a template in an AngularJS directive?如何在 AngularJS 指令中重新渲染模板?
【发布时间】:2014-02-27 21:05:05
【问题描述】:

我创建了一个生成 Twitter 按钮的指令。由于这些按钮上的范围变量可能会更改,因此我需要在按钮发生时重建按钮。目前,我正在使用 jQuery 来empty() 链接元素并重建按钮。

app.directive 'twitterShare', ($timeout, $window) ->
    restrict: 'E'
    template: '<a href="https://twitter.com/share" class="twitter-share-button" data-text="{{ text }}" data-url="{{ url }}">Twitter</a>'
    scope:
        text: '@'
        url: '@'
    link: (scope, el, attrs) ->
        scope.$watch 'text', -> rebuild()
        scope.$watch 'url' , -> rebuild()

        rebuild = ->
            $(".twitter-share-button").remove()
            tweet = $ '<a>'
            .attr 'href', 'https://twitter.com/share'
            .attr 'id', 'tweet'
            .attr 'class', 'twitter-share-button'
            .attr 'data-lang', 'en'
            .attr 'data-count', 'none'
            .text 'Tweet'

            el.prepend tweet
            tweet.attr 'data-text', scope.text
            tweet.attr 'data-url', scope.url
            $window.twttr.widgets.load()

有没有办法让指令完全重新渲染模板?

【问题讨论】:

    标签: javascript jquery angularjs angularjs-directive


    【解决方案1】:

    这是一个可重用的指令,您可以使用该指令在发送事件时重建嵌入的内容:

    app.directive('relinkEvent', function($rootScope) {
        return {
            transclude: 'element',
            restrict: 'A',
            link: function(scope, element, attr, ctrl, transclude) {
                var previousContent = null;
    
                var triggerRelink = function() {
                    if (previousContent) {
                        previousContent.remove();
                        previousContent = null;
                    }
    
                    transclude(function (clone) {
                        element.parent().append(clone);
                        previousContent = clone;
                    });
    
                };
    
                triggerRelink();                
                $rootScope.$on(attr.relinkEvent, triggerRelink);
    
            }
        };
    
    });
    

    这是一个 jsFiddle 演示它是如何工作的:http://jsfiddle.net/robianmcd/ZQeU5/

    请注意每次单击“触发重新链接”按钮时输入框的内容是如何重置的。这是因为每当触发事件时,输入框就会被移除并添加到 DOM 中。

    您可以按原样使用此指令或对其进行修改,使其由scope.$watch() 而不是事件触发。

    【讨论】:

    • 在按照OP的例子使用模板的情况下应该如何使用transclude函数?当我尝试向此示例添加模板时,它似乎没有被使用 - 我猜是因为克隆。
    • 好东西,我真的需要这样的东西。我添加了一个 $compile 选项,以便我可以从角度重新编译组件。我将在底部发布更改。
    【解决方案2】:

    一些建议:

    1. 使用指令模板并将变量绑定到范围,而不是手动创建 HTML。在这种情况下,您不需要重新渲染模板。当范围属性发生变化时,Angular 会自行更新。

    2. 使用attrs.$observe函数在属性值更改时运行一些代码

    【讨论】:

    • 您会注意到我已经在使用模板并绑定范围变量。但是,这并没有重建 Like 按钮,因为如果 iframe 已经存在,Facebook 不会重建它。除非删除并重新创建该 twitter-share-button 元素,否则 $window.twttr.widgets.load() 什么都不做。
    【解决方案3】:

    您尝试执行的操作与指令中的 compile 函数一致,它会重新呈现 html。你试过吗?

    这是一种 hacky 方式,但是在指令上放置一个 ng-if truthy 变量,当你想要渲染时,设置和取消设置truthy变量:

    angularjs: force re-rendering/ full refresh a directive template

    【讨论】:

      【解决方案4】:

      实现此目的的另一种方法是使用 ng-if。

      例如: &lt;myDirective ng-if="renderdirective"&gt;&lt;/myDirective&gt;

      在您的 renderdirective 不正确之前,不会创建该指令。如果您希望删除指令并让它使用新的属性值重新创建,这也可以反过来工作。

      【讨论】:

        【解决方案5】:

        @rob 答案的小变化:

        import * as angular from 'angular';
        
        class ReRenderDirective implements angular.IDirective {
        
          public restrict = 'A';
          public replace = false;
          public transclude = true;
          constructor( private $rootScope: angular.IRootScopeService, private $compile: angular.ICompileService ) {
        
          }
        
          public link = (
            scope: angular.IScope,
            element: angular.IAugmentedJQuery,
            attr: any,
            modelCtrl: any,
            transclude: angular.ITranscludeFunction ) => {
        
            let previousContent = null;
        
            let triggerRelink = () => {
              if ( previousContent ) {
                previousContent.remove();
                previousContent = null;
              }
        
              transclude(( clone ) => {
                element.append( clone );
                previousContent = clone;
        
                element.html( attr.compile );
                this.$compile( element.contents() )( scope );
              } );
        
            };
        
            triggerRelink();
            this.$rootScope.$on( attr.reRender, triggerRelink );
        
          }
        
        }
        
        export function reRenderFactory(): angular.IDirectiveFactory {
        
          var directive = ( $rootScope: angular.IRootScopeService, $compile: angular.ICompileService ) => new ReRenderDirective( $rootScope, $compile );
          directive.$inject = [ '$rootScope', '$compile' ];
          return directive;
        }
        

        将此用于:

        <div re-render="responsive">
          <header-component/>
        </div>
        

        并将其与代码中某处的 $broadcast 结合起来:

        this.$rootScope.$broadcast( 'responsive' );
        

        我所做的是听页面调整大小,然后触发广播。 基于此,我能够将组件的模板从桌面更改为移动。因为示例中的header-component 被嵌入,所以它被重新渲染和重新编译。

        这对我来说就像一个魅力。

        感谢 Rob 让我走上正轨。

        【讨论】:

        • 实际上我发现我可以使用名为ng-if 的本机指令来解决这个问题。我只需要确保呈现具有 ng-if 的标签,并且当它需要重新呈现时,布尔值应该变为 false,然后添加 0 的 $timeout 以将其设置回 true。这样它会重新渲染组件:) :)
        【解决方案6】:

        使用 ng-bind="value" 而不是 {{value}} 刷新了我的指令模板缓存。

        <myDirective><span ng-bind="searchResults.leads.results.length"></span></myDirective>
        

        而不是

        <myDirective><span>{{searchResults.leads.results.length}}</span></myDirective>
        

        【讨论】:

        • 似乎不是一种可靠的归档方式
        猜你喜欢
        • 2013-11-03
        • 1970-01-01
        • 1970-01-01
        • 2019-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多