Angular.js ng-switch-when不能与动态数据一起工作?

31

我正在尝试让Angular根据我的数据生成CSS滑块。我知道数据已经存在并且能够为按钮生成它,但是代码由于某种原因无法填充ng-switch-when。当我检查代码时,我发现这个出现了两次(我知道这是正确的,因为我只有两个项目):

<div ng-repeat="assignment in assignments" ng-animate="'animate'" class="ng-scope">
    <!-- ngSwitchWhen: {{assignment.id}} -->
</div>

我的实际代码:

<div ng-init="thisAssignment='one'">
     <div class="btn-group assignments" style="display: block; text-align: center; margin-bottom: 10px">
         <span ng-repeat="assignment in assignments">
              <button ng-click="thisAssignment = '{{assignment.id}}'" class="btn btn-primary">{{assignment.num}}</button>
         </span>
     </div>

     <div class="well" style="height: 170px;">
         <div ng-switch="thisAssignment">
              <div class="assignments">
                   <div ng-repeat="assignment in assignments" ng-animate="'animate'">
                        <div ng-switch-when='{{assignment.id}}' class="my-switch-animation">
                        <h2>{{assignment.name}}</h2>
                        <p>{{assignment.text}}</p>
                   </div>
              </div>
         </div>  
    </div>  
</div>

编辑:这是我试图模拟的内容,但使用动态数据。 http://plnkr.co/edit/WUCyCN68tDR1YzNnCWyS?p=preview


当您单击按钮时,描述您期望在DOM方面发生的情况。从您的代码中不清楚您正在尝试做什么。您是否尝试维护“已选择”的分配? - Jon Jaques
另外,请注意您在ng-repeat中遮蔽变量。我的意思是,在外部块中,您将字符串“one”分配给assignment,但是在重复器块中,您具有相同名称的迭代器变量assignment,然后在块内进行分配。您应该尝试更改用于跟踪所选内容的属性名称,例如selectedAssignment。 - Jon Jaques
我认为我已经根据你提到的修改了我的代码,但我仍然不太清楚。上面修改后的代码无法工作。 - user1855009
你看了我下面的回答吗?Ng-switch 用于硬编码(非动态)条件。试着查看文档,看看其他指令是否会根据某个条件动态交换 DOM,例如 ng-if。 - Jon Jaques
好的。我不确定你最近的评论是否否定了之前提到的任何内容。我会看一下。对此很抱歉。 - user1855009
2个回答

50

来自docs

请注意,要匹配的属性值不能是表达式。它们被解释为字面字符串值以进行匹配。例如,ng-switch-when="someVal"将匹配字符串"someVal"而不是表达式$scope.someVal的值。

换句话说,ng-switch用于在模板中硬编码条件。

您可以像这样使用它:

<div class="assignments">
  <div ng-repeat="assignment in assignments" ng-animate="'animate'">
    <div ng-switch="assignment.id">
      <div ng-switch-when='1' class="my-switch-animation">
      <h2>{{assignment.name}}</h2>
      <p>{{assignment.text}}</p>
    </div>
  </div>
</div>

现在这可能不完全符合您的使用情况,因此您可能需要重新考虑策略。

Ng-If可能是您需要的 - 同时,您需要注意"隔离"作用域。基本上,当您使用某些指令(如ng-repeat)时,您会创建与其父级隔离的新范围。因此,如果您在重复器内更改thisAssignment,则实际上是在该特定重复块内更改变量,而不是整个控制器。

这里有一个demo,展示了您要做的事情。

请注意,我将所选属性分配给things数组(它只是一个对象)。


更新于12/12/14:添加了一个新的代码块,以澄清ng-switch的使用。上面的代码示例应该被视为不要这样做。
正如我在评论中提到的那样。Switch应该像JavaScript switch一样考虑。它是用于硬编码的切换逻辑。因此,例如在我的示例帖子中,只会有几种类型的帖子。您应该事先知道要切换的值的类型。
<div ng-repeat="post in posts">
  <div ng-switch on="post.type">

    <!-- post.type === 'image' -->
    <div ng-switch-when="image" class="post post-image">
      <img ng-src="{{ post.image }} />
      <div ng-bind="post.content"></div>
    </div>

    <!-- post.type === 'video' -->
    <div ng-switch-when="video" class="post post-video">
      <video ng-src="{{ post.video }} />
      <div ng-bind="post.content"></div>
    </div>
    
    <!-- when above doesn't match -->
    <div ng-switch-default class="post">
      <div ng-bind="post.content"></div>
    </div>
  </div>
</div>

你可以使用ng-if实现相同的功能,但需要根据应用程序的需求决定哪种方式更合适。在这种情况下,后者更加简洁,但也更加复杂,如果模板更为复杂,你会发现它变得更加棘手。基本区别是ng-switch是声明式的,ng-if是命令式的。
<div ng-repeat="post in posts">
  <div class="post" ng-class="{
      'post-image': post.type === 'image', 
      'post-video': post.type === 'video'">
    <video ng-if="post.type === 'video'" ng-src="post.video" />
    <img ng-if="post.type === 'image'" ng-src="post.image" />
    <div ng-bind="post.content" />
  </div>
</div>

啊!非常感谢!有点类似于加入(我想),但它就是无法通过。非常感激。 - user1855009
这个方法能否修改以处理嵌套数据?例如,我想使用ng-repeat来显示用户列表,当点击一个用户时,它会显示一些个人资料信息? - mcneela86
1
为什么我们需要这个功能?我觉得它会促进魔法值...如果开关依赖于i18n翻译的值,那该怎么办? - Cosmin
你能澄清一下你所说的“魔法”值是什么意思吗?switch 语句用于应用程序中的硬编码逻辑。假设你有一个 ng-repeat,它遍历了帖子。而且你有几种类型的帖子。也许有文本类型、图像类型和视频类型。在 switch 语句中,你会 switch on="post.type",然后你的 when 子句将是“text”、“image”和“video”,也许你会将“text”作为默认类型。i18n 不应该参与到这个方程式中,因为你不应该根据用户输入进行切换。 - Jon Jaques
一个“神奇”值是在代码的多个地方硬编码而不是集中为常量的值。 我想使用AngularJS常量作为我的ng-switch-when语句的模型数据。但是我不能,这让我很伤心 :(. 我需要这些常量,但现在这个值是重复的。 - Romain F.
你在原始示例中为什么使用单引号将1括起来?并不是批评,只是好奇是否有实际原因。 - trysis

12

Jon说得没错。Angular不支持动态的ngSwitchWhen值,但我想让它支持。实际上,我发现用自己的指令代替ngSwitchWhen非常简单。它不仅支持动态值,还支持每个语句的多个值(类似于JS switch fall-throughs)。

但需要注意的是,它只在编译时对表达式进行一次求值,因此必须立即返回正确的值。对我来说,这很好,因为我想使用应用程序中其他地方定义的常量。它可能可以修改为动态重新评估表达式,但那需要更多与ngSwitch的测试。

我正在使用Angular 1.3.15,但我在Angular 1.4.7上进行了快速测试,并且也可以正常工作。

Plunker演示

代码

module.directive('jjSwitchWhen', function() {
    // Exact same definition as ngSwitchWhen except for the link fn
    return {
        // Same as ngSwitchWhen
        priority: 1200,
        transclude: 'element',
        require: '^ngSwitch',
        link: function(scope, element, attrs, ctrl, $transclude) {
            var caseStms = scope.$eval(attrs.jjSwitchWhen);
            caseStms = angular.isArray(caseStms) ? caseStms : [caseStms];

            angular.forEach(caseStms, function(caseStm) {
                caseStm = '!' + caseStm;
                ctrl.cases[caseStm] = ctrl.cases[caseStm] || [];
                ctrl.cases[caseStm].push({ transclude: $transclude, element: element });
            });
        }
    };
});

使用方法

控制器
  $scope.types = {
      audio: '.mp3', 
      video: ['.mp4', '.gif'],
      image: ['.jpg', '.png', '.gif'] // Can have multiple matching cases (.gif)
  };
  <div ng-switch="mediaType">
    <div jj-switch-when="types.audio">Audio</div>

    <div jj-switch-when="types.video">Video</div>

    <div jj-switch-when="types.image">Image</div>

    <!-- Even works with ngSwitchWhen -->
    <div ng-switch-when=".docx">Document</div>

    <div ng-switch-default>Invalid Type</div>
  <div>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接