foreach is not flexible with JSON data

2,178 views
Skip to first unread message

Eric

unread,
Dec 29, 2011, 7:12:04 PM12/29/11
to KnockoutJS
OK, let's say I have some JSON data for "person". person can be:
1) array
2) object
3) non-existent

Knockout's foreach seems to provide no flexibility to handle these 3
cases. It should handle them all, in my opinion.

1) foreach loop through array
2) foreach process as 1 element
3) foreach does not process anything

If anyone can provide me an elegant way of handling these 3 cases
without a bunch of if statement or duplicated code, that would be most
helpful. Otherwise, I hope knockout can improve the foreach behavior.

Thank you,
Eric

rpn

unread,
Dec 29, 2011, 8:14:42 PM12/29/11
to knock...@googlegroups.com
Hello-
The foreach binding is a wrapper to the template binding.  In the template binding some work is done to coerce single values into an array.  

When you pass a non-array to the foreach binding it isn't sure quite how to handle it.  This is because the foreach binding can either be specified like:

data-bind="foreach: myItems"  or  data-bind="foreach: { data: myItems, afterRender: myFunction }"

So, when you pass an object, it thinks that you are using the second syntax (passing options).

If you are more explicit about what you are foreaching on by doing:  data-bind="template: { foreach: myItems }" or data-bind="foreach: { data: myItems }", then it will coerce a single value into an array for you.


null values are already handled, as foreach will not do any processing.

In my opinion, the best option though is to normailze your data into an array within your view model.  Maybe if you share some code or a sample we can help.  From your description, in sounds kind of like the results of a web service call can be an array, a single item, or empty.

You would inspect the result and see if it is empty, an array, or a single item and return an empty array, the array, or an array with one item respectively.




Eric Murphy

unread,
Dec 29, 2011, 10:08:33 PM12/29/11
to knock...@googlegroups.com
Thank you for the quick reply.

I looked at your example, and I do not believe this is what I am looking for. There are explicit one value and no value items, which I do not see a need for. This is very confusing to me why this would be done.

If the template can gracefully handle array, non-array and null values all the same, that would be the best solution. For example, with this data:

{
    animals:{
        dog:[
            {
                name:'Rufus'
            },
            {
                name:'Marty'
            }
        ],
        cat:{
            name:'Garfield'
        }
    }
}

In the scope of animals, I should be able to do foreach: dog, foreach: cat, and foreach: mouse without any errors.

I am outputting JSON from XML data, and there is the option to output all values as array, but that would be a poor solution.

Knockout seems pretty awesome so far, this is the only real complaint!

Thank you,
Eric

rpn

unread,
Dec 29, 2011, 10:26:37 PM12/29/11
to knock...@googlegroups.com
Mine was just a sample covering some of the various cases.  Obviously, you would not have a property that always contains nothing.

Here is your sample:


Notice that each uses a slightly different syntax.  You could specify all of them using the third syntax and it would handle arrays, single items, and undefined/null values in the way that you desired.

Eric Murphy

unread,
Dec 29, 2011, 10:38:16 PM12/29/11
to knock...@googlegroups.com
OK, I tried the 3rd syntax, and that fixed my problem! Thank you!

I wish this was the default behavior of foreach, however. Do you think that would be a good thing?

Thanks,
Eric

EntitySpaces

unread,
Dec 29, 2011, 11:35:25 PM12/29/11
to KnockoutJS
I'm not sure I agree, this isn't a foreach problem, for instance. In
C# you wouldn't new a single Person on it and then call foreach( ) on
it. I think this is asking too much of Knockout, as for it not choking
on undefined there I agree, assuming it does, never tried that
actually. But then, I would just by default declare your data as "[]"

Eric Murphy

unread,
Dec 29, 2011, 11:43:57 PM12/29/11
to knock...@googlegroups.com
To clarify, I am using XML to JSON, so depending on if there are 2 or more elements, it will make an array. Otherwise, it makes an object.

I understand what you say, it does not make sense from a programmer's standpoint. However, most if not all JSON generator tools will *not* make an array with one object in it. One reason for this is that the JSON tools have no sense of an XML's schema, for example. It does not know that there can be more than 1 element unless they exist in the data.

I can guarantee there will be more requests for this feature of a more flexible default foreach in the future. JSON generation is not precise enough to write exact JavaScript/Knockout templates for, unfortunately.

Eric

Mark Hahn

unread,
Dec 30, 2011, 12:50:55 AM12/30/11
to knock...@googlegroups.com
In C# you wouldn't new a single Person on it and then call foreach( ) on it.

+1

This appears to be a very narrow particular problem with XML.  You are going to have to translate the XML to JSON with some kind of knowledge of whether it is an array or an object. 

Thank god I don't have to deal with XML anymore.  JSON rocks.

Leandro de los Santos

unread,
Dec 30, 2011, 7:04:55 AM12/30/11
to KnockoutJS

If you can't get a normalized JSON from server (wich in my point of
view it's the correct way to do things), you can do this in JS. From
my point of view foreach must work always with arrays. The propertys
can have values or even be dependantObservables for view interaction,
so if you pretend to iterate propertys, you lose the main capability
of KO wich allows to manage your view from with viewstate's values.

Imagine for example you need to allow user an option to enable /
disable each animal... If you make foreach over object property's you
lose the option to put a property "Selected" in each animal... (Hope
be clear with the example).

About "Json generation it's not precise"... I don't agree with this...
JSON generation is a serialization of your information, if you can't
control the structure of your information, you can't do anything... So
maybe the solution it's to look another JSON serialization or process
your information to have a normalized message.

I make an example with your JSON data in Javascript, this example
iterate over the JSON information and create a normalized array with
"category" (property, dog, cat..) and "name".

http://jsfiddle.net/kCqKR/11/

As you can see, it's really simple, just know how your messages come,
read your data and do the structures you need for your viewmodel...

Hope helps you.

Leandro
PS: Sorry for my english!!! I hope not be rude with my forms.
Reply all
Reply to author
Forward
0 new messages