We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
Absolutely fantastic example...
Looks like just what we need, went down the first couple of routes too. Good work
Thanks for this. I was just looking to do a modal dialog that a user could "Cancel" out of to prevent edits to the model, and this solves the problem perfectly. Cheers!
Great stuff Ryan. Very slick!
Wicked! I went route #1, temporary object as my VM main array was simple.
I will try this approach you suggest.
Very cool. Have you thought about a function that would convert every property into a protectedObservable() automatically? Sort of like the ko.mapping plugin - but instead of making every property observable(), it makes them protectedObservable().
Hi Steve-
I guess I hadn't thought too much about it. I have done it with mapping options in the mapping plugin. Might be nice to find a way to do it more automatically though.
Hmm, I though the mapping options in the mapping plugin force you to specify the property you're overriding (I could be mistaken on that). In this case, we'd want it to automatically apply to *all* properties like how the mapping plugin applies observable() to all properties. Maybe looping over the properties might be sufficient for simple cases.
That was great, as usual.
Great idea and a nice example of a simple list, which is just what I needed. I wanted a simple list with modal edit and this will do nicely.
Thought Id share my Fiddle in which I added a few functions I thought Id need....
http://jsfiddle.net/PAEz/D5...
...just incase any other copy and paste coders look at this ;P
Is there any easy way to convert an already observable properties to protectedObservable. In other words, I have a JS model coming from a service that has a object graph of other complex objects and arrays. I use the ko.mapping plugin to convert the entire JS Model's properties and objects to observable. However, I would like on some cases to take one (not all) of its object properties and convert the observable to protectedObservable (because it will be used in a dialog with save/cancel-to edit).
Do I just unwrap each property I want protectedObservable and convert it to a protected Observable?
I added the following to the top of protectedObservable, but it just seems to much extra work:
var unwrap = ko.utils.unwrapObservable;
if (!ko.isObservable(initialValue)) {
_actualValue = ko.observable(initialValue);
_tempValue = initialValue;
}
else {
_actualValue = initialValue;
_tempValue = unwrap(initialValue);
}
Would ko.mapping.toJS(model) still work with protectedObservable?
@Corey - you could use the "create" callback in the mapping options (http://knockoutjs.com/docum... to control how your objects are created.
Otherwise, after using the mapping plugin and before calling applyBindings, you could redefine any variables that you want to be protected and just pass in the current value directly (unwrap it yourself).
Would be happy to help if you can't get it working.
Ryan,
Nice demo, I have a similar need, however, I have to be able to add/delete Columns dynamically. This fiddle has all the behaviors I need except the Remove Column:
http://jsfiddle.net/rniemey...
can you help?
Here is a quick sample with the ability to remove a column: http://jsfiddle.net/rniemey.... Hope this helps.
@Ryan thanks for the inspiration.
I put a spin on this and came up with a new concept. I created a 'protectObservable' function that takes in an existing observable and wraps it instead of creating a new observable to wrap. I also created a 'protectModel' function that takes in a model and wraps all of its writeable observables with the protected version and simply copies all of the other properties. This way, the new protected model can be bound to in the same way as the original model. This version only wraps the children of the model, not the entire tree.
Lets assume you have some observable 'selectedItem' that holds a model with observable properties and is data bound to some editable form. We can simply create a new observable 'protectedItem' set to 'ko.protectModel(selectedItem())' and change the binding to use 'protectedItem' instead of 'selectedItem'. Making 'protectedItem' a computedObservable would probably be a good choice here. The only other change required is to call 'protectedItem().commit()' whenever a save is performed. You can still save individual properties if you need to using 'protectedItem().myProp.commit();'.
This could probably use further refinement. In particular, I don't like that 'protectModel' adds functions to the cloned model, which would clobber any existing properties with those names.
You can find the code here: http://pastebin.com/Q1xSpyAM
Is that possible that you can provide an example on how you init protectedItem and how you set it to ko.protectModel(selectedItem())?
@Troy - thanks for sharing. That is an interesting twist on the idea. protectModel looks quite useful.
This was very helpful. Any idea if this is getting put into the actual library?
@facebook-730122396 I don't think that this would get added to the actual library. We are trying to keep it as lean as possible and encourage plugins to be created on top of it. Glad that it was useful to you!
Hey Ryan, I am new to knockout and am really digging your blog posts. They have been REALLY helpful. One question about this approach is that I am not sure how it will apply to observable arrays. Would love an example. Thanks again!
@Ericj observableArrays are a little bit tricky with this method. If you are dealing with primitive items or simple objects (no observables), then you can do something like: http://jsfiddle.net/rniemey... (primitives) or http://jsfiddle.net/rniemey... (simple objects). So, the idea is that we make a copy of the object and either commit it as our cached value or reset back to our cached value.
If you were dealing with complex items that contain observables, then you would have to let it know how to map the copy to your items like: http://jsfiddle.net/rniemey....
Ryan... thanks so much for your help. I took your example and applied it to my scenario. Here is the fiddle.. http://jsfiddle.net/digitzf...
I have two questions.
1) Is there a way to have it load and work properly without a blank ticket needed.
2) I cannot for the life of me figure out why my formattedPrice is not working.
Thanks again!
@Ericj - in your computed observable you will want to pass the second argument to make sure that your "this" is correct. Also, since price is an observable, you will want to access its a function to get its value. I added a + in front of it as well to ensure that it will start as 0. Here is the updated fiddle: http://jsfiddle.net/rniemey.... Maybe you can explain the blank ticket issue further. It looks like maybe you have that part sorted out?
This works great in most of the cases, but it doesn't seem to work well with knockout validation... I can't find a way to get the validation to work. Does anyone know how to make this protectedObservable works with knockout validation?
I think the issue could be:
the protectedObservable needs to commit the change first in order to trigger the validation logic. However, as we are still updating the content, commit the change does not seem to be a good way to do it. Without having the value get updated, the validation will not kick in.
Still try to find a good solution to resolve this issue...
It seems like as long as the validation is relies on notification of the value update, the actual value will must get updated in order for the validation rule to work unless I write a validation control to handle during the commit stage...
@Art - to work with validation, one option would be to alter how the protected observable works where you would always update the real observable and you can choose to revert back to a cached value. Here is a sample that uses a "revertableObservable" with the validation plugin. The first name has a minimum length or 2 and maxium length of 10.
http://jsfiddle.net/rniemey.... Hope this helps.
Thanks! It works great! I combine your suggestion with Troy's protectModel, and everything works automatically now.
Brilliant. What about a pull request to the official Knockout? Or a plugin, maybe.
Thank you!
I might think about a plugin. The core needs to stay lean and mean. There are certainly lots of features that could be added on top of this basic sample.
that's a great example of knockout usage
Any chance you know what the problem is here?
http://stackoverflow.com/qu...
Knockout core is trying to stay lean, focused, and maintainable. We have added extensibility points that help with these scenarios (like the ability to extend observables with something like the "editable" function that I showed in the video from here: http://www.knockmeout.net/2.... There is room for plugins to be built on top of KO for functionality that is outside the core. For example, here is a library that does things like this: https://github.com/romanych....
Based on Art's protectModel, I came up with this little method to simplify the commit() while using Ryan's protectedObservable:
ko.protectedCommit = function (m) { for (var key in m) { var prop = m[key]; if (ko.isWriteableObservable(prop)) { prop.commit(); } }};
I add one function to return the tempvalue for data validation before commit:
result.current = function () { return _tempValue; }
Hi,
is there an easy way to collect all protectedObservables from my ViewModel? This way i don't have to enumerate all properties by hand...
I usually do something like:
http://jsfiddle.net/rniemey.... Look at acceptItemEdit and cancelItemEdit.
Im using a different approach to handle Arrays is this good?
var OriginalExperience;
var Experience = function (currentJobs, pastJobs, practices) {
var self = this;
self.CurrentJobs = ko.observableArray(currentJobs);
self.PastJobs = ko.observableArray(pastJobs);
self.Practices = ko.observableArray(practices);
self.InEdition = ko.observable(false);
self.ToggleEdition = function () {
self.InEdition(!self.InEdition());
if (self.InEdition()) {
OriginalExperience = ko.mapping.toJSON(self);
}
};
self.CancelEdition = function () {
ko.mapping.fromJSON(OriginalExperience, {}, self);
self.InEdition(false);
};
//Add, Remove.....
}
@dpineda - I don't see a problem with that technique. I actually do something somewhat similar, but without the mapping plugin in much of my code.
But how to use it in save method.
Usually I do like this:
$.ajax("api/values/" + item.id(), {
data: ko.toJSON(item),
type: "pop",
contentType: "application/json",
success: function (responce) {},
error: function (xhr, textStatus, errorThrown) {}
});
item was something like this:
function Friend(id, name) {
var self = this;
self.id = id;
self.name = name;
}
But now item is like this:
function Friend(id, name) {
var self = this;
self.id = ko.protectedObservable(id);
self.name = ko.protectedObservable(name);
}
and
data: ko.toJSON(item),
is not correct now.
What to do?
@Sereban - you will still retrieve the currently committed values of the protectedObservables when you do ko.toJSON on them. Do you need to get at the temporary values?
Hi,
I am using the protectedobservable in my project from Accept/Cancel scenario. It works great. But now, I need to subscribe when a particular property changes. Since, all the properties of the selecteditem are private, I am unable to do so.
Any suggestions how can I accomplish this task.
@Navneet - you can still subscribe to the protectedObservable itself and it will notify you when the value changes, as it is reading the underlying private observable. If you need to get at the temp value, then you can choose to expose it by attaching it to "result" (result.tempValue = _tempValue). Then, you can access it like myProtectedObservable.tempValue. Hope that helps!
result.tempValue = ko.observable(_tempValue);
isn't it?
@Marco - I guess that I had meant doing var _tempValue = ko.observable(initialValue); at the beginning and then finally doing result.tempValue = _tempValue. Same thing really, but you are right that the tempValue has to be observable. Thanks!
@norbiu - you are running into the fact that all bindings on an element fire together (until KO 3.0). In this case changing isLoading triggers all of the bindings on that element and ends up resetting the value back to the original. Here is a slightly different pattern that uses a real "temp" observable attached to the original. So, you can bind against "editValue" as well as "editValue.temp". In the fiddle, I made the save function commit the value after 1 second, as if the request to the server returned successfully. Hope this helps: http://jsfiddle.net/rniemey...
Thanks a lot, this helps. PS: before I saw this comment, I spend 1 day to find out it's the problem of knockout version.
Amazing article, really helped me out - couldn't appreciate this more!!
This is neat, rpn! The only feature I think writable dependent observables are missing by default are the knockout array methods for when you declare a WDO with an array value(I've included them in my knockout.live plugin, for example), but they opened a lot of power for MVVM :)