Git Product home page Git Product logo

knockout-repeat's Introduction

REPEAT binding for Knockout

The repeat binding can replace foreach in many instances and is faster and simpler for some tasks. In contrast to foreach:

  • repeat does not create a new binding context. Therefore, the variables for the binding context ($data, $parent, and $parents) are unaffected. Instead, you can access the current item using $item() and $index.
  • repeat operates on the outer HTML instead of the inner HTML.
  • repeat can either loop a number of times (count) or iterate over an array or observableArray (foreach).
  • repeat avoids excessive re-rendering of DOM nodes by updating only the child bindings when the view model changes.

Here’s a comparison between foreach and repeat for a data table:

<table>
    <tbody data-bind="foreach: data">
        <tr data-bind="foreach: $parent.columns">
            <td data-bind="text: $parent[$data.propertyName]"></td>
        </tr>
    </tbody>
</table>

<table>
    <tbody>
        <tr data-bind="repeat: { foreach: data, item: '$row' }">
            <td data-bind="repeat: { foreach: columns, item: '$col' }"
                data-repeat-bind="text: $row()[$col().propertyName]"></td>
        </tr>
    </tbody>
</table>

Main Parameters

The repeat binding accepts a single parameter of the number of repetitions or an array to iterate. It also accepts an object literal with these parameters provided through the count or foreach property. If the parameter is an observable, the repeat binding will add or remove elements whenever you update it. Here are the main parameters:

  • foreach — an array (or observableArray) over which to iterate, or the number of repetitions
  • count — the number of repetitions. If both the foreach and (non-zero) count parameters are given, the count value takes precedence. This allows you to provide an array using foreach but always output a fixed number of items, even if it’s larger than the array length.
  • limit — an upper limit on the number of repetitions, if non-zero (optional)

The following optional parameters do not support updates (and can’t be observable):

  • reverse — if true, the elements will be repeated in reverse order, with the lowest value at the bottom and items added to the top (default is false)
  • step — the increment value (default is 1)
  • index — the name of the index context property (default is $index; see section below)
  • item — the name of the context property used to access the item in the array (default is $item; see section below)
  • bind — the binding used for the repeated elements (see section below)

Context Properties

The repeat binding makes the following context properties available to bindings in the repeated nodes.

  • $index — the zero-based index of the current item. The name of this property can be changed using the index option.
  • $item — the array item matching the current index. This property in available only when an array is supplied to the repeat binding. It is a pseudo-observable, which can be passed directly to bindings that accept observables (most do), including two-way bindings; or the item value can be accessed using observable syntax: $item(). The name of this property can be changed using the item option.

Repeated Element Binding

The repeat binding allows you to specify the binding for the repeated elements in a number of ways. Note that you cannot do this in the normal way you set additional bindings for an element—for example, <span data-bind="repeat: 5, text: $index"> will not set the text of the repeated elements and will probably generate an error.

  1. The simplest and cleanest way is to use a data-repeat-bind attribute, which becomes the data-bind attribute of the repeated elements.

    <span data-bind="repeat: 5" data-repeat-bind="text: $index">
  2. Similarly, you can specify a binding string using the bind parameter to repeat.

    <span data-bind="repeat: { count: 5, bind: 'text: $index' }">
  3. If you are using a custom binding provider that doesn’t support the standard binding method of using a data-bind attribute, you can specify the binding for repeated elements using a function provided through the bind parameter to repeat. If using this option with foreach, the first parameter to the function is the item and the second is the index. If used with just count, the first parameter is the index. In both cases, the last parameter to the function is the binding context object, which is useful if you want to access additional context properties such as $parent.

    <span data-bind="repeat: { count: 5, bind: function($index) { return { text: $index } } }">
    <div data-bind="repeat: { foreach: availableCountries, item: '$country',
        bind: function($country) { return { css: { sel: $country() == selectedCountry() } } } }">
        <span data-bind="text: $index+1"></span>. <span data-bind="text: $country"></span>
    </div>

License and Contact

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

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

knockout-repeat's People

Contributors

mbest avatar mente 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

knockout-repeat's Issues

add step, limit, and reverse options

step: <number> increments by number instead of 1: so step: 2 would go 0,2,4,...
limit: <number> ends at number of items even if the count is greater. This is useful for arrays when you only want to include the first number of items.
reverse: true starts at the bottom and moves up. Possibly make this a separate binding (repeat.reversed) because it would not be easy to support changing the order.

Virtual element points to the previous virtual element used

Hey,

Here is what I have (maybe I did something wrong).

<div data-bind="repeat:{ foreach: projects, index:'$projIndex', item:'$projItem' }">
    <table>
        <thead>
            ...
        </thead>
        <tbody>
            <tr data-bind="repeat:{ foreach: $projItem().fte, item: '$fteItem', index:'$fteIndex' }" data-repeat-bind="visible:$root.fteVisible($projItem())">
                <td>
                    <a data-bind="visible:$root.isDeleteVisible($fteItem()),click:$root.removeFTEFromProject.bind($fteItem(),$fteIndex)" title="Remove"></a>
                    <span data-bind="text:$fteItem().code"></span>
                </td>
            </tr>
        </tbody>
    </table>
</div>

My problem resides on the 'a' element.
$root.isDeleteVisible($fteItem()) - this call is totally fine and returns the correct context.

$root.removeFTEFromProject.bind($fteItem(),$fteIndex) - this one returns me the $root model instead of the item like the previous.

I can change it to $root.removeFTEFromProject alone and I will still get the $root model.
My current workaround is to do a ko.contextFor(event.currentTarget).$fteItem() and then I can continue with what I was doing.

If i put an inline comment with the 'div' repeat, the second repeat will instead point to the $projItem instead.

<!-- ko foreach: {data: projects, as: '$projItem'} -->
    <table>
        <thead>
            ...
        </thead>
        <tbody>
            <tr data-bind="repeat:{ foreach: $projItem().fte, item: '$fteItem', index:'$fteIndex' }" data-repeat-bind="visible:$root.fteVisible($projItem())">
                <td>
                    <a data-bind="visible:$root.isDeleteVisible($fteItem()),click:$root.removeFTEFromProject.bind($projItem(),$fteIndex)" title="Remove"></a>
                    <span data-bind="text:$fteItem().code"></span>
                </td>
            </tr>
        </tbody>
    </table>

I am using KO 3.1

UPDATE:

  • This kinda changes the problem in itself.

I just noticed that if I have the binding like: click:$root.removeFTEFromProject.bind(-1,$projItem(),$fteIndex)
Now everything is working and sending the following signature:

function removeFTEFromProject(projItem, fteIndex, rootModel, event){}

My question is why is the 1st element ("-1") being discarded every time?

Cheers,
J

Ajax

Hi,
I'm relatively new to knockout.
Does your repeater allow updates ?

I'm getting "Uncaught TypeError: Cannot read property 'allRepeatNodes' of undefined" if I update the collection in an ajax request.

I'm trying to a create a jquery ui tab without using some of the template based solutions I found. Your repeater is the only example I found that creates elements outside the data bound element, which fits jquery ui' tab' ul/li/div pattern

I'll give a simple sample if its necessary.

Thanks!

No beforeRemove

With template bindings + foreach, you can introduce animated removals through the beforeRemove callback. I can't seem to figure out a way to accomplish this using the init or update callbacks that currently exist.

`arrayChange` support

Knockout 3.0 added arrayChange subscriptions to get deltas of arrays. Since foreach will support these, in a scenario that a small delta happens (push one item), that foreach could actually be quite a bit more performant than knockout-repeat if I'm understanding the code correctly.

Or would it not? without a new binding context, individual items would be bound to an instance in the array, if that instance didn't change in the example of the push one item on the array, then would the result end up in just one DOM node insertion?

remove and removeAll breaks communication with first element of array

So I have observableArray of new LineItem() instances, and when I try to use .remove(that instance) or .removeAll([all,instances,to,remove]) it removes the element(-s) and then first element of array has one way binding, i.e. if I change input nothing changes but if I change in console everything reflects on screen.

Is this some kind of specific bug to what someone has a workaround, or this is something that's need to be fixed? (If someone can point me where should put my hands on to fix this, I will be glad to do so, speed of rendering is important to what I'm doing)

I switched back to foreach for time being because of shipping deadlines.

Bindings

Hi,
Thanks for all the help thus far (I'm still having issues calling jqTab but I'm working on it)

I have an issue binding an input control to my modelview:

<div data-bind="repeat: {foreach: currencies, item: '$currency', bind: 'attr: { id: $currency().Guid} '}">
  <input data-bind="value: $currency().Description" />
</div>

Which correctly fills in the Description for each currency, but when I eventually do a:

ko.toJSON({ data: self.currencies })

It doesn't return the changed data.

Thanks!

Handling Observable Arrays

Is the plugin meant to handle observable arrays as they change - other than calling the remove / push methods?

When adding new items (push), the markup gets updated and same goes for remove. However, when I reset the observable with a new array (data coming back from the server), nothing gets updated.

This may be a separate issue, but I've also noticed when using "push" on the observable array, my custom bindings "update" method gets called without fail, but on "remove", it does not. Is this the intended behavior?

Any help would be appreciated. Thanks for the plugin - it's quick!

[enhancement] Add missing bower.json.

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

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-repeat 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-repeat",
  "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!

ko.dataFor()

I've noticed that I can't use ko.dataFor($(element)[0]) to get the data for this item.

Why I need it:

.on('mousedown', 'row', function (e) {
                    var tempElement = ko.dataFor(this);
                    if (e.ctrlKey && e.shiftKey) {
                        //selectMultiple(tempElement, false);
                    } else if (e.ctrlKey) {
                        //selectSingle(tempElement, false);
                    } else if (e.shiftKey) {
                        //selectMultiple(tempElement, true);
                    } else {
                        selectSingle(tempElement, true);
                    }
                })

As you can see, I can't put this within element data-repeat-bind because of ctrlKey and shiftKey detection.

Could you provide any thought how could I work around this? Thank you.

P.S. Internet explorer 11 was having lag spikes using foreach for about 2 sec, now it's around 400-500ms, major improvement. (i.e. for generated 100 rows each having generated 10-13 columns).

Ko 3.4 out of the box inline templating with foreach versus knockout-repeat

Hello,

Quick question. I am using knockout 3.4 with have a large dynamic table that is having performance issues. I know the main root cause of the issue is that the table is re-rendered multiple times. In researching the issue, I came across this project as a way to help with repeating rows.

My question is if this custom binding is still relevant with Knockout 3.4.. or did some of the improvements this binding provides solved by inline template improvements in the knockout 3.2/3.3

Thanks
Greg

Repeat Multiple Items

Its posible to repeat multiple items?

this is my scenario

Item Descripton

!-- Begin Group repetition: --

March Income March Outcome March Total April Income April Outcome April Total

...
!-- Ends repetition --

Total Income Total Outcome Total

So what you i need to repeat it's a group of th not only one.
I was doing it with virtual elements and foreach binding.

Templating support in knockout-repeat?

Hi,

This binding works really much more faster than foreach binding in my case. What I'm missing (or just don't understand), if it is possible to use this binding together with templating?

Currently I have something like

<table>
    <tbody data-bind="template: { name: getProductTemplate, foreach: productList }"></tbody>
</table>

<script type="text/html" id="productType1">
    <tr>
        <td>Product of Type1</td>
        <td>
            <a data-bind="text: $data.name, attr:{href:prodHref}"></a>
        </td>
    </tr>
</script>

<script type="text/html" id="productType2">
    <tr>
        <td>Product of Type2</td>
        <td>
            <span data-bind="text: $data.name"></span>
        </td>
    </tr>
</script>

Is it possible to transform this use case to repeat-binding base?

Love it but...

Love this binding, helped me with a container less binding not working in IE. In fact I think this is more useful more times than foreach is.

However I don't much care for the "bind" value being provided as a string. Couldn't we provide that as a func accepting... $data (or $item?), $parent. Eg: bind: function($data, $parent) { return { attr : { title : $data.name } } } ?

Fyi, heres how i used it to get optgroups working (in IE, wasn't a problem in Chrome, FF etc)

 <select data-bind="value : zone" name="zone">
              <option>@_("Select")</option>
              <optgroup data-bind="repeat : { foreach : sites, bind : 'attr: { label : $item().name }, foreach : $item().items' }">
                  <option data-bind="text : localizedName, value : id" />
              </optgroup>
            </select>

It'd be nice if the options bindings supported optgroups out of the box!

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.