Git Product home page Git Product logo

knockout-switch-case's Introduction

SWITCH/CASE bindings for Knockout

Knockout includes only the if and ifnot bindings for control flow. The switch and case bindings provide a flexible and powerful control flow mechanism that can simplify your code.

Let's start with some examples. Here we want to display a different message based on a status value:

<div data-bind="switch: orderStatus">
    <div data-bind="case: 'shipped'">
        Your order has been shipped. Your tracking number is <span data-bind="text: trackingNumber"></span>.
    </div>
    <div data-bind="case: 'pending'">
        Your order is being processed. Please be patient.
    </div>
    <div data-bind="case: 'incomplete'">
        Your order could not be processed. Please go back and complete the missing data.
    </div>
    <div data-bind="case: $default">
        Please call customer service to determine the status of your order.
    </div>
</div>

Here's an equivalent example using source data values:

<div data-bind="switch: true">
    <div data-bind="case: trackingNumber">
        Your order has been shipped. Your tracking number is <span data-bind="text: trackingNumber"></span>.
    </div>
    <div data-bind="case: isReady">
        Your order is being processed. Please be patient.
    </div>
    <div data-bind="casenot: isComplete">
        Your order could not be processed. Please go back and complete the missing data.
    </div>
    <div data-bind="case: $else">
        Your order could not be processed. Please go back and complete the missing data.
    </div>
</div>

A switch block can contain any number of case blocks. No more than one case block will be used. The contents of the remaining blocks will be cleared. Both switch and case take a single parameter. In most cases the values of the two parameters are matched against each other to determine which case block to use. The first block (top-down) to match is used; subsequent blocks are cleared. Here, in detail, is how the values are matched:

  1. If the case value is the special value $else or $default, the block is used if no non-default blocks were used. There can be multiple blocks with $else or $default and they can be in any order.
  2. If the switch value is boolean (true or false), each case value's "truthiness" is matched against the switch value.
  3. If the case value is boolean (and the switch value is not boolean), the case value is used as is. The special variable $value can be used in a case expression to refer to the switch value. The block will be used if the expression is true.
  4. If the case value is an array, the block will be used if the switch value matches (strict) an item in the array.
  5. Otherwise, the block will be used if the case value matches the switch value (loose comparison).

If you want a block to be used based on the parameter not matching, you can use the casenot binding. This works similarly to case, except that the result of the value matching is reversed.

Here are some more examples. This example demonstrates items 1, 3, 4, and 5 above and uses container-less bindings:

<!-- ko switch: somevalue -->
<!-- ko case: $default -->
    Value doesn't match
<!-- /ko -->
<!-- ko case: 'foo' -->
    Value is foo
<!-- /ko -->
<!-- ko case: 'bar' -->
    Value is bar
<!-- /ko -->
<!-- ko case: ['baz', 'qux'] -->
    Value is either baz or qux
<!-- /ko -->
<!-- ko case: $value.length == 3 -->
    Value is a three-letter word
<!-- /ko -->
<!-- /ko -->

This example demonstrates item 2 (also see the second example above):

<!-- ko switch: isReady -->
    <div data-bind="case: true">You are ready!</div>
    <div data-bind="case: false">You are not ready!</div>
<!-- /ko -->

Controlling visibility instead of inclusion

You can use the case.visible and casenot.visible bindings to conditionally show an element. For example:

<!-- ko switch: true -->
    <p data-bind="case.visible: isReady">You are ready!</p>
    <p data-bind="case.visible: $else">You are not ready!</p>
<!-- /ko -->

Switch shortcut when using Knockout 3.x

When using Knockout 3.0+, you do not need to include the true value for the switch binding. For example:

<!-- ko switch -->
    <!-- ko case: 'abc' -->Value matched<!-- /ko -->
    <!-- ko case: $else -->Value didn't match<!-- /ko -->
<!-- /ko -->

Extending switch/case to control other UI aspects

ko.bindingHandlers.switch.makeCaseHandler can be used to create new bindings that control any UI aspect. It returns a new binding handler object that you should assign to a ko.bindingHandlers property. It takes three parameters:

  • binding This is the name of the binding to wrap (case wraps the if binding, for example).
  • isNot (optional) If set to true, the new binding will reverse the result of the value matching (like casenot).
  • makeValueAccessor (optional) If the wrapped binding needs the value specified in a certain format, you can provide a function that accepts a value parameter and returns a valueAccessor function. The default looks like this: function (value) { return function() { return value } }

License: MIT (http://www.opensource.org/licenses/mit-license.php)

Michael Best
https://github.com/mbest
[email protected]

knockout-switch-case's People

Contributors

mbest avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

knockout-switch-case's Issues

$value is not an observable, so dependent knockout elements do not get updated

I had a switch on a length of an array, everything works great except that in a case $default I had a reference to $value which did not get updated in some cases.
Example:

               <!-- ko switch: interface.calendar.events.listShown().length -->
                    <!-- ko case: 0 -->
                   No event
                    <!-- /ko -->
                    <!-- ko case: 1 -->
                    One event
                    <!-- /ko -->
                    <!-- ko case: $default -->
                    <span data-bind="text:$value"></span> events in that period
                    <!-- /ko -->
                <!-- /ko -->

The span value did not update in some cases. listShown is a ko.computed array with trackArrayChanges enabled, and if I removed $value and changed that to interface.calendar.events.listShown().length it would work, so I guess this is a bug because the $value is not an observable.

The fix I did in your plugin was:
Adding
newContext.$value = ko.observable();
Before
contexts.push(newContext);

And replacing
context.$value = value;
With
context.$value(value);

(I also did the same for the switchBindings, but I don't think it's required)

After my changes everything seemed to work perfectly.

Thank you for that plugin!

Why are non-active elements cleared and not hidden?

If the element output by the case has a fixed-height or padding, it breaks the visual output by introducing extra space.

Would it not make more sense to hide elements that are not active versus clearing them?

[enhancement] Add missing bower.json.

Hey, maintainer(s) of mbest/knockout-switch-case!

We at VersionEye are working hard to keep up the quality of the bower's registry.

We just finished our initial analysis of the quality of the Bower.io registry:

7530 - registered packages, 224 of them doesnt exists anymore;

We analysed 7306 existing packages and 1070 of them don't have bower.json on the master branch ( that's where a Bower client pulls a data ).

Sadly, your library mbest/knockout-switch-case is one of them.

Can you spare 15 minutes to help us to make Bower better?

Just add a new file bower.json and change attributes.

{
  "name": "mbest/knockout-switch-case",
  "version": "1.0.0",
  "main": "path/to/main.css",
  "description": "please add it",
  "license": "Eclipse",
  "ignore": [
    ".jshintrc",
    "**/*.txt"
  ],
  "dependencies": {
    "<dependency_name>": "<semantic_version>",
    "<dependency_name>": "<Local_folder>",
    "<dependency_name>": "<package>"
  },
  "devDependencies": {
    "<test-framework-name>": "<version>"
  }
}

Read more about bower.json on the official spefication and nodejs semver library has great examples of proper versioning.

NB! Please validate your bower.json with jsonlint before commiting your updates.

Thank you!

Timo,
twitter: @versioneye
email: [email protected]
VersionEye - no more legacy software!

Bower support

Michael, not sure we've ever spoken directly, but I've been following and I love (and sincerely appreciate) all the work you've been doing on KO.

A bower.json file would be fairly trivial for this plugin and would permit registering with the Bower registry.

Which I think would be super-cool. :)

One item array doesn't seem to work

Hello,
So I heavily enjoy this plugin and think it works wonders, however I think I might have found a bug in the code. As the title suggests it seems to not behave when working with one item arrays. This may sound counterintuitive since the point of a switch case is to switch between multiple item, but hear me out. Currently I'm making a modal to be modular. so it looks like this (in terms of pug)

<!-- ko foreach: array -->
        <!-- ko switch: $data.type -->
          <!-- ko case: "text"  -->
          fieldset
            input(type="text", data-bind="textInput: propValue")
          <!-- /ko -->
          <!-- ko case: "checkbox"  -->
          fieldset
            input(type="checkbox", data-bind="checked: propValue")
          <!-- /ko -->
          <!-- ko case: $else  -->
          fieldset
            select(data-bind="options: propValue")
          <!-- /ko -->
        <!-- /ko -->
<!-- /ko -->

array is an array that contains an object that holds really 2 properties: type a string value that is used to check the type of input field it needs to be, and propValue the value of the input value assigned to it. So it works in terms if there are at least 2 items in the array. But what happens if all I need is one field? If I were to ask the user for just one thing, in this case an email. it does something wonky by applying it to all the cases. so we end up getting an email text, an email checkbox, and an email dropbox. See example below for charity of what I mean:

screen shot 2016-12-09 at 10 04 29 am

I still think it a great plugin and I want to keep using it, so any help would be much appreciated.

Thanks for your time

Embedded Knockout-Switch with Let Binding Overwrites $caseValue on child context then breaks the UI

Original issue reported on knockout issues: knockout/knockout#2550.

Hi,

I spent the weekend tracking down a insidious issue on our enterprise-scale application that is based on Knockout.js. I finally was able to create a minimal reproducible example on jsfiddle. Situation:

  1. Outer knockout-switch structure.
  2. Let binding block within the active case statement in the outer switch structure.
  3. Inner switch statement within the let binding context.
  4. Change a condition that causes one of the case statements on the inner switch statement to go to false.
  5. Outer switch structure fails, and none of its case blocks, including the $default case block, are rendered, leaving the user interface blank.

What I see happening in the knockout code:

When the condition is changed, the child context is updated via ko.bindingContext updateContext method. This ends up calling:
ko.utils.extend(self, parentContext);

This copies all properties from the parent contexts to the child context. The $caseValue binding context variable from the outer context gets overwritten in the inner case binding context. THEN the inner case binding sets its $caseValue to false (which is now pointing to the outer switch case binding $caseValue), thus causing the outer switch binding's case statements to permanently go to false, including the $default.

This seems like a bug to me, but could possibly a misuse / misunderstanding of the let binding. It seems like the inner binding context's $caseValue properties should never be overwritten by an outer switch binding's $caseValue properties, regardless of the presence of a let binding.

At any rate, it's a pitfall because binding context mutation is happening unintentionally that is causing undesirable behavior, and extremely difficult to track down. Please see this fiddle for a very simple demo of the problem:

https://jsfiddle.net/gsdLyftc/10/

case fall-through

Is it possible to somehow implement a fall-through as in native JS?

switch (x) {
  case 1: //fall-through
  case 2: //fall-through
  case 3: //fall-through
    console.log('1, 2 or 3');
  break;
  case 4:
    console.log('4');
  break;
}

Will this be in trunk?

It's not hard to find that many users just demand a switch-case or simply if-else, just to improve a little bit of readability over this kind of codes:

<!-- ko ifnot: states.isLoading -->
<!-- ko if: 0 in states.selectedItems() -->
<!-- ko ifnot: states.visibleItems().length -->
  <span data-bind="text: something"></span>
<!-- /ko -->
<!-- /ko -->

<!-- ko ifnot: 0 in states.selectedTokens() -->
  <span data-bind="text: somethingElse"></span>
<!-- /ko -->

<!-- ko foreach: states.visibleItems -->
  <div class="item">blah ...</div>
<!-- /ko -->
<!-- /ko -->
<!-- /ko -->

While freedom plugin fix this, it breaks some others.

Or we just need to wait for ko 3.0?

Nuget support

I'd appreciate if this useful library was registered as NuGet package on nuget.org.
With your permission I'd be happy to register it for you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.