Reputation: 509
I'm having an architectural issue with some form design using Knockout for data-binding. I have a pretty massive project at work and for simplicity's sake, let's say I'm hitting an API endpoint (not RESTful though) and getting back a JSON response. I am currently running this response through a custom mapping function to turn all properties into observables. The form is working fine but I now want to add validation using Knockout Validation. We haven't constructed all the different rules yet, but the answer I'd love some insight into is the best way to go through my observable map to add the rules. The additional headache is that the response that comes back has properties that vary based on some settings on the server that can be updated in the UI, so I won't have observables ready for what I could potentially need. I know that is terribly confusing, so perhaps a vague, contrived example will help:
Precondition: Setting X is enabled on page load
JSON response:
results: [
{ a: 1 },
{ b: 2 },
{ c: 3 }
]
Processing:
var arr = ko.observableArray([]);
$.get('/endpoint/').then(function(data) {
data.results.forEach(function(result) {
arr.push(mapToObservables(result));
});
});
Setting Y is enabled, property "c" is no longer relevant but "d" is, so the JSON that needs to go back is:
updates: [
{ a: 1 },
{ b: 3 },(updated value)
{ d: 4 } (new value)
]
I was observing properties "a, b, c" initially, so "a, b" are good to go since their state is still known. But property "d" needs to be added due to a settings change and I wasn't already watching for it.
This example is very contrived, but I don't want to have any IP issues so hopefully it makes sense. The real application is much larger and the different GETs/POSTs to the same endpoint could result in 6 or 7 different states. It doesn't seem very scalable/maintainable to check "is X set? Then set up Y's and Z's properties as empty observables with the appropriate rules, just in case" (and likewise for the other possible scenarios).
I really hope this makes even a little sense to someone who could guide me. I came into this project not even knowing what Knockout was or how the previous app worked so it's been a bit crazy trying to assimilate all the relevant knowledge. Thanks to you all in advance!
EDIT:
I've accepted Roy's answer below because it's closest to what I'm looking for, but for a bit more clarity, imagine this scenario. I hit an endpoint which could send back properties a
, b
, c
, or d
(but always a subset, never the full set). If I get back a
and b
, I can easily use ko.mapping
to make them observable in case they change. What I need to be able to do, though, is allow the user to basically get rid of b
and choose c
instead, for example. I can't think of a clean way to "set up" observables for c
and d
at page load to allow for their use, IF the user wants to use them. The only thing that comes to mind is that I set up observables for all possible fields in my application, even though 90% of the time, half of them won't be used. Or I suppose I could use a pubsub system like postbox to dynamically create the needed observables at runtime. I know there is some overhead to using observables, but they seem fairly lightweight to me, so I'm not sure which of these methods is "best" when both performance and time of development are considered.
Upvotes: 0
Views: 138
Reputation: 43881
Is this the sort of behavior you're looking for?
var d1 = {
a: 1,
b: 2
};
var d2 = {
a: 'oo',
c: 'ah'
};
var vm = {
data:ko.observable()
};
vm.data(ko.mapping.fromJS(d1));
setTimeout(function() {
vm.data(ko.mapping.fromJS(d2));
}, 1000);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div data-bind="if:data().a">A:<span data-bind="text:data().a"></span>
</div>
<div data-bind="if:data().b">B:<span data-bind="text:data().b"></span>
</div>
<div data-bind="if:data().c">C:<span data-bind="text:data().c"></span>
</div>
Upvotes: 1