beauXjames
beauXjames

Reputation: 8428

Use ko.mapping to create an UN-observed array with observables?

I am setting up my model with the ko.mapping utility.

When passing in my primary data, consider one of my properties to be an array. This array is one object per 7 days of the week, so I know this set will not change nor be rearranged. I can easily generate this array as either a copy of the JSON object with flatted properties or as an observableArray with either observed or flat properties, but what I can't seem to do through the mapping utility is create a flat array with a mix of both basic properties and observed properties.

I have attempted various permutations of mapping options, but it still looks as though I'm going to have to map this array without or just bite my incessant and anal urge to have this complex model mapped exactly how I want it in the way I want to map it.

To better show the issue:

var PrimaryViewModelMapping = {
    copy: ['KeyProperty', 'ArrayOfDays'],
    create: function(options) { return new PrimaryViewModel(options.data); }
}

This will give me a flat copy of the array in my instance of the PrimaryViewModel without any extra effort...but no control over the properties within the ArrayOfDays without looping through the array in my declaration of PrimaryViewModel and doing stuff.

To continue:

var PrimaryViewModelMapping = {
    copy: ['KeyProperty'],
    observe: ['ArrayOfDays'],
    create: function(options) { return new PrimaryViewModel(options.data); }
}

This will nicely package up my ArrayOfDays as an observable array yet keep all of its 'each' properties flattened.

The next attempt was the following:

var PrimaryViewModelMapping = {
    copy: ['KeyProperty'],
    'ArrayOfDays': {
        copy: ['Date'],
        observe: ['TotalDuration'],
        create: function(options) {
            return new DayArrayModel(options.data);
        }
    }
}

This makes an observable array and gives me full control over what is observed (TotalDuration) and what isn't observed (KeyProperty) and within the declaration of my object DayArrayModel, just about anything else I may want to do...

BUT the ArrayOfDays is still an observable Array. I don't need nor really want it to be.

Understand that the topic here is specific to the mapping plugin and I don't want to create loops within the PrimaryViewModel declaration to handle this...which I know I could do. Just wondering if I've stumbled upon a 'feature-request' or if I'm simply not getting it.

Thanks.

Upvotes: 0

Views: 1295

Answers (2)

beauXjames
beauXjames

Reputation: 8428

I came to a wall with the topic of mapping in Knockout. I did climb this wall, however I remain perplexed. From the perspective of an advanced use case, the mapping plugin seems to do nothing more than obfuscate the connection between the data and the viewModel bound by Knockout.

I discovered the list below (and added comments) from http://www.coderenaissance.com/ which also seems to have another variation of a mapping plugin that seems to not have gotten much attention of yet. I do like the creator's insight, for it meshes well with my own experience(s)...

  • It was slow on large view models, particularly in older browsers(IE7/8) and on large arrays. (not as big a deal…yet)
  • Creating viewmodels wasn't a one step process... after calling mapping you'd then have to extend the viewmodel further. (Occasionally, for small storage models, I didn’t need to do this, however for additional properties/functions I had this additional need to continue to follow our existing patterns.)
  • It provided no easy way to organize the viewmodel creation code which becomes a problem on larger viewmodels. (One of my biggest annoyances…I was really hoping using combinations of mapping options I could really cut down on the repetition of properties)
  • It didn't allow much customization in how the viewmodel was created and what customization there was was confusing and not intuitive to use. (The manipulation of the view model has been absolutely unavoidable. The timing/order of mapping procedures wasn’t always as predictable as I hoped it would be. Really gets confusing when you start cascading the mappings together.)

So, the punchline for my first question...or joke, as it seems to me now...is that there is no spoon. As a result of my findings, I removed the attempt to use ko.mapping to act as my schema initialization and only use it for prepping my initial trigger from the view before my ko.applybindings command. At this point, I use a combination of arrayMap and observable(array) to fill my objects...just like I used to. For now, I've gained the knowledge of when not to use the mapping plugin.

Upvotes: 0

Jeroen
Jeroen

Reputation: 63830

If you insist on wanting to prevent the ArrayOfDays to be a regular array, you could utilize the ko.utils.arrayMap method. However, what you want goes quite against what the mapping plugin does, so it's going to get a bit verbose.

The problem is that mapping options don't allow you to specify how individual properties are constructed, you can only tell the plugin to include or exclude properties, or how to generate elements in an array. So your first step is not to generate the main view model directly. Instead, you could do this:

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

    var mappingOptions = {
        'exclude': ['ArrayOfDays']
    };

    // Map everything, except the array
    ko.mapping.fromJS(data, mappingOptions, self);

    // Do the array itself, but prevent it from becoming an *observable* array
    self.ArrayOfDays = ko.utils.arrayMap(data.ArrayOfDays, function(item) { return new DayViewModel(item); });
};

This does a few things:

  1. Create self to prevent any nasty this issues;
  2. Set an option variable that'll tell KO we'll handle the array ourselves.
  3. Map everything except the array, on top of self.
  4. Handle the array ourselves.

The key here is that ko.utils.arrayMap will return a flat array, not an observableArray.

Here's a fiddle to demonstrate this.

As a foot note, it's unclear to me why you'd want something like this (premature optimization perhaps?).

Upvotes: 2

Related Questions