0

I'm having a problem getting an array of information stored properly as JSON.

I made a fiddle to illustrate the problem. Enter a set of tags and take a look at the console to see the output.

More explanation:

So I have an input that takes in a comma-separated list of tags, which I then format.

function createTagArray() {
   // given an input value of 'tag1, tag2, tag3'
   // returns array = ['tag1', 'tag2', 'tag3']
} 

I thought what I needed to do next was the following: loop over the array and create a 'tag' object for each item which also includes an id for the tag and the id of the contact the tag is associated with.

Each object is pushed to tags, an observable array.

function single_tag(id, contactId, tagLabel) {
    var self = this;

    self.id = id;
    self.contactId = contactId;
    self.tagLabel = tagLabel;
}

function createTags() {
  var array = createTagArray();

  for (var i = 0; i < array.length; i++) {
    self.tags().push(new single_tag(uuid.generate(), self.contactId, array[i]));
  }
}

Then, I converted it into JSON

self.contactInformation = function() {
  return ko.toJS({
    "id": self.contactId,
    "firstname": self.firstname(),
    "lastname": self.lastname(),
    ... other fields ...
    "tags": self.tags(),
  })
}

But, when I inspect the console output of calling this function, tags is a collection of arrays, not a nice json object.

Messed up tags JSON

How do I get it formatted correctly?

I tried this suggestion, and the tag json is structured correctly, but it is stored with escaped quotes, so that seems wrong.

Thanks for all the help!

2
  • 1
    FYI, your example is broken. There's a typo in the form binding. sumbit => submit Commented Jul 21, 2015 at 21:26
  • Thanks! That explains why it was trying to post the form. Commented Jul 21, 2015 at 21:30

3 Answers 3

1

I would recommend you knockout.mapping plugin for KO, it allow map complicated JSON structure to view model, even without declarations.

From the documentation

Let’s say you have a JavaScript object that looks like this:

var data = {
    name: 'Scot',
    children: [
        { id : 1, name : 'Alicw' }
    ]
}

You can map this to a view model without any problems:

var viewModel = ko.mapping.fromJS(data);

Now, let’s say the data is updated to be without any typos:

var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}

Two things have happened here: name was changed from Scot to Scott and children[0].name was changed from Alicw to the typo-free Alice. You can update viewModel based on this new data:

ko.mapping.fromJS(data, viewModel);

And name would have changed as expected. However, in the children array, the child (Alicw) would have been completely removed and a new one (Alice) added. This is not completely what you would have expected. Instead, you would have expected that only the name property of the child was updated from Alicw to Alice, not that the entire child was replaced!

...

To solve this, you can specify which key the mapping plugin should use to determine if an object is new or old. You would set it up like this:

var mapping = {
    'children': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        }
    }
}
var viewModel = ko.mapping.fromJS(data, mapping);
Sign up to request clarification or add additional context in comments.

Comments

0

In the jsfiddle you were using Knockout 3.0 which doesn't have support for textInput. This was added in 3.2. To use version 3.2 you need to use a cdn such as this: http://cdnjs.com/libraries/knockout

There was typeo in your binding. sumbit should be submit.

There was a problem with your constructor for single_tag. id was not used so I removed it:

function single_tag(contactId, tagLabel) {
    var self = this;

    self.contactId = contactId;
    self.tagLabel = tagLabel;
}

Currently also contactId is not set because the observable has not been set to a value.

To convert to JSON you need to use ko.toJSON instead of ko.toJS:

self.contactInformation = function() {
    return ko.toJSON({
      "firstname": self.firstname(),
      "tags": self.tags(),
    })
}

Now when the console writes out an array appears:

{
  "firstname":"test",
  "tags":[
    {"tagLabel":"test1"},
    {"tagLabel":"test2"},
    {"tagLabel":"test3"}
  ]
}

JsFiddle

1 Comment

Thanks for taking a look. I did try using toJSON, but this stores as an escaped string in my JSON db. toJS stores correctly, but obviously there is the tag problem. Great catch on the knockout version, by the way!
0

So my problem was more basic than I was realizing. I'm using JSON Server to serve up my data, and I was pulling information from two parts of the database (contacts & tags).

When I tried to update my tags, I was trying to apply them to a property that didn't exist on the contact JSON in my database. Posting the tags separately worked though.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.