Question

Need to add new items to an observable array but have them added to the top of the list, not the bottom as in this example. Any ideas please? Have tried .reverse() but obviously there's an issue with the array being dynamically created and displayed...

http://jsfiddle.net/CSFuF/1/ with .reverse() not working

http://jsfiddle.net/CSFuF/ without .reverse()

<!-- End view/edit employees details -->
<ul data-bind="foreach: parents.reverse()">
     <h3 data-bind="visible: $index() == 0">Parents</h3>

    <!-- add / remove parent -->
    <li>          
        <fieldset>
            <h4 data-bind="text: $index()"></h4>
              <p style="float: left">
                <button data-bind='disable: !(children().length > 0), click: toggleChildren'><span data-bind="visible: toggle">-</span><span data-bind="visible: !toggle()">+</span>
                </button>
                <button data-bind='disable: !toggle(), click: addChild'>Add child (+)</button>
            </p>
            <!-- add remove child -->
            <!-- ko if: toggle -->
            <ul class="qtr" data-bind="foreach: children.reverse()">
                 <h4 data-bind="visible: $index() == 0">Children</h4>

                <li>
                    <fieldset>
                        <h4 data-bind="text: $index()"></h4>
                        <button data-bind='click: removeChild'>Remove child (-)</button>
                    </fieldset>
                </li>
            </ul>
            <!-- /ko -->
            <p style="float: right">
                <button data-bind='click: removeParent'>Remove parent (-)</button>
            </p>

        </fieldset>
    </li>
</ul>
<p style="float: right">
    <button data-bind='click: addParent'>Add parent (+)</button>
</p>

JS:

function Parent(children) {
    var self = this;
    //self.name = ko.observable(name);
    self.toggle = ko.observable(true);
    self.children = ko.observableArray(children);
    self.addChild = function () {
        self.children.push(new Child("", self));
    }
    self.removeParent = function (parent) {
        vm.removeParent(self);
    };
    self.removeChild = function (child) {
        self.children.remove(child);
    }
    self.toggleChildren = function () {
        self.toggle(!self.toggle());
    };
}

function Child(name, parent) {
    var self = this;
    self.parent = parent;
    self.removeChild = function () {
        self.parent.removeChild(self);
    };
}

function ParentChildViewModel() {
    var self = this;
    self.parents = ko.observableArray([]);
    self.addParent = function () {
        self.parents.push(new Parent());
    };
    self.removeParent = function (parent) {
        self.parents.remove(parent);
    }
};

var vm = new ParentChildViewModel();
ko.applyBindings(vm);
Was it helpful?

Solution

Calling reverse() on an observable array reverses the underlying array in place and doesn't return the array. So binding to its return value didn't render anything.

To insert into the beginning of the observable array, use unshift().

You're also using $index to display the index of the array item being rendered. But because you're always traversing from beginning to end, it will always go from 0 to length - 1.

Here's what it looks like with unshift(). I've added a name property and inserted a counter in it so that you can see the order of items in the arrays...

HTML

<!-- End view/edit employees details -->
<ul data-bind="foreach: parents">
     <h3 data-bind="visible: $index == 0">Parents</h3>

    <!-- add / remove parent -->
    <li>          
        <fieldset>
            <h4 data-bind="text: name"></h4>
              <p style="float: left">
                <button data-bind='disable: !(children().length > 0), click: toggleChildren'><span data-bind="visible: toggle">-</span><span data-bind="visible: !toggle()">+</span>
                </button>
                <button data-bind='disable: !toggle(), click: addChild'>Add child (+)</button>
            </p>
            <!-- add remove child -->
            <!-- ko if: toggle -->
            <ul class="qtr" data-bind="foreach: children">
                 <h4 data-bind="visible: $index == 0">Children</h4>

                <li>
                    <fieldset>
                        <h4 data-bind="text: name"></h4>
                        <button data-bind='click: removeChild'>Remove child (-)</button>
                    </fieldset>
                </li>
            </ul>
            <!-- /ko -->
            <p style="float: right">
                <button data-bind='click: removeParent'>Remove parent (-)</button>
            </p>

        </fieldset>
    </li>
</ul>
<p style="float: right">
    <button data-bind='click: addParent'>Add parent (+)</button>
</p>

JavaScript

function Parent(name, children) {
    var self = this;
    self.name = name;
    var childCount = 0;
    //self.name = ko.observable(name);
    self.toggle = ko.observable(true);
    self.children = ko.observableArray(children);
    self.addChild = function () {
        self.children.unshift(new Child(++childCount, self));
    }
    self.removeParent = function (parent) {
        vm.removeParent(self);
    };
    self.removeChild = function (child) {
        self.children.remove(child);
    }
    self.toggleChildren = function () {
        self.toggle(!self.toggle());
    };
}

function Child(name, parent) {
    var self = this;
    self.parent = parent;
    self.name = name;
    self.removeChild = function () {
        self.parent.removeChild(self);
    };
}

function ParentChildViewModel() {
    var self = this;
    var parentCount = 0;
    self.parents = ko.observableArray([]);
    self.addParent = function () {
        self.parents.unshift(new Parent(++parentCount));
    };
    self.removeParent = function (parent) {
        self.parents.remove(parent);
    }
};

var vm = new ParentChildViewModel();
ko.applyBindings(vm);

Fiddle... http://jsfiddle.net/CSFuF/2/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top