Angular.js ng-switch-when not working with dynamic data?

北战南征 提交于 2019-11-27 19:02:16

From the docs

Be aware that the attribute values to match against cannot be expressions. They are interpreted as literal string values to match against. For example, ng-switch-when="someVal" will match against the string "someVal" not against the value of the expression $scope.someVal.

So in other words, ng-switch is for hardcoding conditions in your templates.

You would use it like so:

<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>

Now this might not fit your use case exactly, so it's possible you'll have to rethink your strategy.

Ng-If is probably what you need — also, you need to be aware of "isolated" scopes. Basically when you use certain directives, like ng-repeat, you create new scopes which are isolated from their parents. So if you change thisAssignmentinside a repeater, you're actually changing the variable inside that specific repeat block and not the whole controller.

Here's a demo of what you're going for.

Notice I assign the selected property to the things array (it's just an object).


Update 12/12/14: Adding a new block of code to clarify the use of ng-switch. The code example above should be considered what not to do.

As I mentioned in my comment. Switch should be thought about exactly like a JavaScript switch. It's for hardcoded switching logic. So for instance in my example posts, there are only going to be a few types of posts. You should know a head of time the types of values you are going to be switching on.

<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>

You could implement this same functionality with ng-if, it's your job to decide what makes sense within your application. In this case the latter is much more succinct, but also more complicated, and you could see it getting much more hairy if the template were any more complex. Basic distinction is ng-switch is declarative, ng-if is imperative.

<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>

Jon is definitely right on. Angular does not support dynamic ngSwitchWhen values. But I wanted it to. I found it actually exceptionally simple to use my own directive in place of ngSwitchWhen. Not only does it support dynamic values but it supports multiple values for each statement (similar to JS switch fall-throughs).

One caveat, it only evaluates the expression once upon compile time, so you must return the correct value immediately. For my purposes this was fine as I was wanting to use constants defined elsewhere in the application. It could probably be modified to dynamically re-evaluate the expressions but that would require more testing with ngSwitch.

I am use angular 1.3.15 but I ran a quick test with angular 1.4.7 and it worked fine there as well.

Plunker Demo

The Code

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 });
            });
        }
    };
});

Usage

Controller
  $scope.types = {
      audio: '.mp3', 
      video: ['.mp4', '.gif'],
      image: ['.jpg', '.png', '.gif'] // Can have multiple matching cases (.gif)
  };
Template
  <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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!