Knockout validation setError on computed observable after applyBindings - validation

I'm trying to manually set an error on a computed observable using Knockout Validation but the validation message isn't displaying. I need to be able to set the error after apply bindings has been called and the group set.
var viewModel = {
computedTest: ko.computed(function(){
return 'Test'
})
};
viewModel.errors = ko.validation.group(viewModel);
ko.applyBindings(viewModel);
viewModel.computedTest.extend({ validatable: true });
viewModel.computedTest.setError('oops');
viewModel.errors.showAllMessages(true);
Using this example a validationMessage doesn't get displayed for the computedTest observable.
I believe the reason is because the validation group hasn't doesn't know that computedTest is now extended. But I'm not sure how to refresh the group so that the error message is displayed.
Here's a better example: https://jsfiddle.net/onbyc67h/.
As you can see if you set the .extend({ validatable: true }) before applyBindings is run a message is displayed, but if you do it after one isn't.
Thanks

What is going on is completely logical: when you apply bindings, the different bound properties are subscribed to changes of existing observables. So, if you create a new observable after binding, there is no way for ko to discover and subscribe to it. Take into account that what the validation extenders do is creating new observables, which can be subscribed. But, if you create them after binding, as explained, they can not be subscribed by the binders.
The only thing that you could do would be to unbind and rebind, but this is not advisable at all.

Related

How to correctly subscribe to Changed sequence of ReactiveObject?

Another question about ReactiveUi. I have a ViewModel for an edit form. Model is ReactiveObject. I want to enable savecommand only when changes of object was take place. My try:
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Any();
But when the form appears the SaveCommand is already enabled. Where my mistake?
You want to use Switch not SelectMany. SelectMany will not unsubscribe from the previous client. It will merge events from all clients. Switch unsubscribes from the previous client before it subscribes to the next.
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Switch()
.Any();
For example the following code makes it clear. Let's say we have a class called AudioChannel It generates audio frames we can can process and send to the speaker.
public class IAudioChannel {
public IObservable<AudioFrame> AudioFrameObservable {get;}
}
Then we might have a list of audio nodes that the user can select but we only want the most current sending audio to the speaker. The below class makes available the currently selected audio node as an observable.
public class AudioListViewModel {
public class IObservable<IAudioChannel> CurrentAudioChannelObservable {get;}
}
Now consider the following code
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.SelectMany(current=>current.AudioFrameObservable)
.Subscribe(frame=>frame.Play());
vs
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.Select(current=>current.AudioFrameObservable)
.Switch()
.Subscribe(frame=>frame.Play());
In the first version as we change the selection of audio nodes we add more and more subscriptions. The audio output quickly becomes a garbled mess of mixed channels. In the second version only one channel is subscribed to at a time and the audio output only plays the output from a single channel.
Many people make this mistake when starting out with RX. For example I found a bug in the ReactiveUI framework that used SelectMany instead of Switch.
However
There is a built in way within ReactiveUI to achieve this in a clear way
There is actually another way to achieve what you want and I will put it in another answer just to show you how to use ReactiveUI.
var canSaveCommand =
this
.WhenAnyObservable(vm => vm.CurrentClient.Changed)
.StartWith(false);
Note that null doesn't have to be explicity handled though you should start with false to make sure a value exists when no observable is available to start with.
WhenAnyObservable
WhenAnyObservable acts a lot like the Rx operator CombineLatest, in
that it watches one or multiple observables and allows you to define a
projection based on the latest value from each. WhenAnyObservable
differs from CombineLatest in that its parameters are expressions,
rather than direct references to the target observables. The impact of
this difference is that the watch set up by WhenAnyObservable is not
tied to the specific observable instances present at the time of
subscription. That is, the observable pointed to by the expression can
be replaced later, and the results of the new observable will still be
captured. An example of where this can come in handy is when a view
wants to observe an observable on a viewmodel, but the viewmodel can
be replaced during the view's lifetime. Rather than needing to
resubscribe to the target observable after every change of viewmodel,
you can use WhenAnyObservable to specify the 'path' to watch. This
allows you to use a single subscription in the view, regardless of the
life of the target viewmodel.
Try changing your Select to a SelectMany. That will then give you an Observable of the changes to be passed into Any instead of an Observable of an Observable of the changes to be passed into Any.

RxJS. Creating an Observable from 2 properties

I'm trying to wrap my head around reactive programming and observables.
What is the reactive way to solve the following scenario?
I have an object with 2 properties.
At anytime, one, both or neither of these properties can be set.
Each of these properties dispach events that I can listen to.
Only when both properties are set I want to listen to their update events and run some kind of aggregation on their properties.
One possible solution is to create a stream from each property and then combine the streams using combineLatest.
combineLatest will not produce a value until both inputs have produced a value. After both inputs have produced a value, the stream will update every time either value changes. See code below:
const property1$ = Rx.Observable.fromEvent(button1, 'click');
const property2$ = Rx.Observable.fromEvent(button2, 'click');
const aggregateWhenBothAreClicked$ = property1$
.combineLatest(property2$)
.map([property1,property2])=>doStuff(property1,property2))
The doStuff function will not be called until both buttons have been clicked. Once both buttons have been clicked, the doStuff function will be called everytime afterwards.

JayData with KendoGrid: entity set events not firing? Bug?

If I assigned a callback to an entity set event:
myContext.Items.beforeDelete = function(){ alert('before delete');}
myContext.Items.beforeUpdate = function(){ alert('before update');}
I get the alert messages if I delete or update a record. But if I use that entity set with a Kendo grid, I do not get any of the events? Is this a bug, or am I doing something wrong?
dataSource: myContext.Items.filter('it.IsDeleted == false').asKendoDataSource();
You've found the correct post, but it's the documentation not a workaround :).
The code you've tried isn't working probably because you should have written it before creating an instance of the context (for example by assigning the event handler in the model definition just like in the docs).
The solution (or work around) is to use the Entity Type events instead of the Entity Set events. I am not sure if this is a bug or not, but there is a clear work around.
See:
http://jaydata.org/blog/jaydata-event-handlers

I need a global event when data binding occurs in Knockout

I am a newbie to knockoutjs. I have searched examples and so far no luck. I have a page that is a data collection form with the values bound using knockout. What I am trying to do is provide the user with a flag letting him know data is modified and that it needs to be saved. In the app a user may pull down the form and display the data from the server and use it only as information. In other cases he may modify that data. I want to display a label that says something like "data has been modified" to the user once any binding has changed plus if he tries to navigate away from the page I want to warn him the changes will be lost. Is there some event I can subscribe to that tells me when any value has been changed in the model?
Thanks,
Terry
Take a look at Ryan Niemeyer's Dirty Flag. It might be what you are looking for. An example of his method can be seen in this jsFiddle.
this.dirtyItems = ko.computed(function() {
return ko.utils.arrayFilter(this.items(), function(item) {
return item.dirtyFlag.isDirty();
});
}, this);
More info can be found in this SO thread: Knockout isDirty example, using dynamic viewmodule from mapping plugin

How to show feedback/error messages in a backbone application

I'm working on a simple CRUD proof of concept with Rails/Backbone/JST templating. I've been able to find a lot of examples up to this point. But after much searching and reading, I've yet to find a good example of how to handle these scenarios:
info message: new item successfully added to list (shown on list screen)
info message: item successfully deleted from list
error message: problem with field(s) entry
field level error message: problem with entry
The Backbone objects are:
Collection (of "post" Models) -> Model ("post" object) -> List/Edit/New Views (and a JST template for each of these views)
So, I'm looking for a high level description of how I should organize my code and templates to achieve the level of messaging desired. I already have a handle on how to perform my validation routine on the form inputs whenever they change. But not sure what do with the error messages now that I have them.
Here is the approach I'm considering. Not sure if it's a good one:
Create a "Message" Model, which maps to a "View", which is a sub-view (if that's possible) on my existing views. This view/model can display page level messages and errors in the first three scenarios I mention above. Not sure if it's feasible to have a "sub-view" and how to handle the templating for that. But if it's possible, the parent templates could include the "message" sub-template. The message view could show/hide the sub-template based on the state of the message model. Feasible? Stupid?
For the fourth scenario, the model validation will return an error object with specific messages per each erroneous field each time a "model.set" is called by form field changes. I don't want to interrupt the "model.set" but I do want to display the error message(s) next to each field. I want to know how to factor my edit/new template and Post model/view in such a way that I don't violate the MVC pattern. I.e. I don't want to put references to DOM elements in the wrong plage.
Sorry if this is vague. If you're inclined to help, let me know what code snippets could be helpful (or other details) and I'll provide them.
You create a global eventbus. When ever an error appears trigger an event. Your view that should show the message listen to the events on this eventbus. Doing so, your error message view dont needs to know all of your collection and vice versa. The eventbus is simple:
var eventBus = _.extend({}, Backbone.Events);
Add it to your collection and trigger it when ever add was called:
var myCollection = Backbone.Collection.extend({
initialize: function([],eventbus){
this.bind('add', function(obj){eventbus.trigger('added', obj)}
}
})
Take also a look at the article: http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

Resources