【问题标题】:Writing a *wrapper* directive declarativly AngularJS以声明方式编写 *wrapper* 指令 AngularJS
【发布时间】:2013-02-09 02:03:23
【问题描述】:

我正在使用 AngularJS 和 Reveal.js 创建幻灯片。显示要求表格

<div class="slides">
    <section>
    </section>
</div>

用于水平幻灯片。虽然垂直幻灯片有两个部分:

<div class="slides">
    <section>
        <section>
        </section>
    </section>
</div>

我正在使用 Angular 渲染此页面:

<div ng-app='myApp' class="reveal">
    <div class="slides" ng-controller='MyController'>
        <section slide ng-repeat="slide in slides">
        </section>
    </div>
</div>

<script src="https://raw.github.com/hakimel/reveal.js/master/js/reveal.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.js"></script>
</body>
</html>

具有多个步骤的幻灯片应该是垂直的。所有其他都应该是水平的。控制器返回步骤并设置 Reveal:

function MyController($scope) {
    $scope.slides = [
        { 'steps': ['a'] },
        { 'steps': ['b1', 'b2'] },
        { 'steps': ['c1'] }
    ];
    setTimeout(function() {
            Reveal.initialize({
                loop: false,
                transition: Reveal.getQueryHash().transition || 'none'
            });
    }, .1 * 1000);
}

指令需要在步骤周围添加一个新元素和属性。这是我丑陋的、令人尴尬的、命令式的、类似 jQuery 的指令:

app.directive('slide', function () {

var wrapContent = function (content) {
    return '<h1>' + content + '</h1>';
};

return {
    restrict: 'A'
    ,link: function (scope, element, attrs, controller) {
    // resorting to imparative jQuery way
    if (scope.slide.steps.length == 1) {
        element.html(
        wrapContent(scope.slide.steps[0])
        );
    } else {
        var sections = '';
        for (i=0,len=scope.slide.steps.length; i < len; ++i) {
        sections +=
            '<section ' +
            function () {
            result = '';
            if (i !== len-1) {
                result = 'data-autoslide="1000" ';
            }
            return result;
            }() +
            '>' +
            wrapContent(scope.slide.steps[i]) +
            '</section>';
        }
        element.html(sections);
    }
    }
}
});

如何编写它使其看起来像 Angular 代码? jsfiddle

我尝试编译、链接、替换、ng-switch 都无济于事。

【问题讨论】:

    标签: angularjs angularjs-directive


    【解决方案1】:

    由于您在作用域上的 slides 属性中定义了所有部分,因此我可能会将整个幻灯片移动到指令中。

    <div ng-app='myApp' class='reveal' ng-controller='MyController'>
      <div slideshow='slides'></div>
    </div>
    

    在指令本身内部,我将遍历每张幻灯片并创建一个&lt;section&gt; 元素。对于每张幻灯片,遍历这些步骤并创建一个&lt;section&gt; 元素(如果有多个)或一个&lt;h1&gt; 元素(如果只有一个)。它可能看起来像这样:

    for (var i = 0; i < slides.length; i++) {
      var section = angular.element("<section>");
      var steps = scope.slides[i].steps;
    
      if (steps.length == 1) {
        var content = angular.element("<h1>").html(steps[0]);
        section.append(content);
      } else {
        for (var j = 0; j < steps.length; j++) {
          var subSection = angular.element("<section>");
          if (j < steps.length - 1)
            subSection.attr('data-autoslide', '1000');
          var content = angular.element("<h1>").html(steps[j]);
          subSection.append(content);
          section.append(subSection);
        }
      }
    }
    

    然后您可以将section 附加到指令的元素中。并且由于在指令内部,我们知道 DOM 已经构建完成,我们可以将调用从控制器(在控制器中进行 DOM 操作或库调用通常不是最好的主意)转移到指令本身。

    把它们放在一起,再加上几个小的改动,你最终可能会得到这个 jsFiddle 上的代码:http://jsfiddle.net/BinaryMuse/CXqAb/

    虽然您可以在指令中以更具声明性的风格编写此代码,但我认为这可能更具可读性,并且当与需要特定 DOM 结构的第三方库(如 Reveal)集成时,会更容易创建一个可预测且干净的 DOM 结构,而不是使用一堆散布在其中的 ngShows 和 ngSwitches 的 DOM 元素。

    【讨论】:

    • 在链接末尾初始化 3 方库消除了愚蠢的setTimeout。谢谢。 OTOH,这仍然比角度更重要。
    • 毫无疑问,这是必要的,而不是您在 Angular 中看到的经典声明式风格。但是,Reveal.js 在它所期望的 DOM 上是如此的具体,并且渲染的逻辑非常复杂,我可能会在实际项目中这样写,因为我相信它更具可读性。 (实际上我还没有成功创建该指令的声明版本。)
    猜你喜欢
    • 1970-01-01
    • 2013-05-15
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多