tony09uk
tony09uk

Reputation: 2991

knockout js cannot delete after adding to observable array

I am trying to complete some basic add and delete functionality using knockout js.

I am using asp mvc, the knockout mappings plugin and returning a simple list of strings as part of my viewModel

Currently I get three items from the server and the functionality I've created allows me to delete each of those items. I can also add an item. But if I try to delete an item i have added in the KO script I cannot delete it.

After completing research and some tests I guess i'm using the observable's incorrectly. I altered my code to pass ko.observable(""), but that has not worked. What am I doing wrong?

values on load

Array[4]0: "test 1"1: "test 2"2: "test 3"length: 4__proto__: Array[0]

values after clicking add

Array[4]0: "test 1"1: "test 2"2: "test 3"3: c()length: 4__proto__: Array[0]

ko script

    var vm = function (data) {
        var self = this;

        ko.mapping.fromJS(data, {}, self);

        this.deleteBulletPoint = function (bulletPoint) {
            self.BulletPoints.remove(bulletPoint)
        }

        this.addEmptyBulletPoint = function () {
            self.BulletPoints.push(ko.observable(""));
            console.log(self.BulletPoints())
        }
    }

HTML

<div class="col-lg-6 col-md-6 col-sm-12">
    <h4>Bullet Points</h4>
    <div id="oneLinedescriptions" class="input_fields_wrap">
        <!-- ko foreach: BulletPoints -->
        <div class="form-group">
            <div class="input-group">
                <input type="text" data-bind="value: $data" class="form-control">
                <span data-bind="click: $parent.deleteBulletPoint" class="input-group-addon btn">-</span>
            </div>
        </div>
        <!-- /ko -->
    </div>
    <button id="btnAddDescription" data-bind="click: addEmptyBulletPoint" type="button" class="btn btn-default add_field_button col-lg-12 animate-off">Add New Bullet Point</button>
</div>

EDIT

I have removed $parent but the below error was returned

Uncaught ReferenceError: Unable to process binding "foreach: function (){return BulletPoints }" Message: Unable to process binding "click: function (){return deleteBulletPoint }" Message: deleteBulletPoint is not defined

In addition to this I have been able to add new empty elements but they are not updated when a user changes the value. is this because the element I am adding is not observable? And if so how do I get round it?

Upvotes: 1

Views: 609

Answers (2)

Jason Spake
Jason Spake

Reputation: 4304

The problems you're having are related to the way ko.mapping treats arrays of primitive types. By design only properties that are part of an object get mapped to observables so an array of strings will become an observableArray of strings. It will not become an observableArray of observable strings.

In order to add/remove items from an array where the items themselves are observable you'll have to make your BulletPoints array an array of objects having the string as a property within:

data = { BulletPoints: [{value: "test1"}, {value: "test2"}] }

Here's a working example: jsfiddle

Upvotes: 1

Daniel Orme&#241;o
Daniel Orme&#241;o

Reputation: 2778

I've added a snippet to ilustrate how you add and remove using an Observable Array with Knockout, the majority of the code is straight from yours so you are in the right track.

Note that when binding to the methods, in this case, there's no need to reference the $parent as you are not using nested scopes. Also, when adding, you just need to pass in plain text, as the observableArray is expecting an object.

If you work with more complex types, when adding you'll need to pass the object itself, and reference its properties from it inside the scope of the iterator, you can read more about it here.

Hope this helps.

var vm = function (data) {
    var self = this;
    
    this.BulletPoints = ko.observableArray(["test1", "test2", "test3"]);

    this.deleteBulletPoint = function (bulletPoint) {
        self.BulletPoints.remove(bulletPoint)
    }

    this.addEmptyBulletPoint = function () {
        const c = self.BulletPoints().length + 1;
        self.BulletPoints.push("test "+c);
    }
}

ko.applyBindings(vm);

        
a {
  cursor: pointer;
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<h4>Bullet Points <span><a data-bind="click: addEmptyBulletPoint">Add +</a></span></h4>
<div data-bind="foreach: BulletPoints">
   <div class="input-group">
      <h3 data-bind="text: $data"></h3>
      <a data-bind="click: deleteBulletPoint">Delete</a>
  </div>
<div>

Upvotes: 2

Related Questions