How do I tell ReactiveUI to update bindings?
Normally, I would do something like this:
string _instructorNameInput;
public string InstructorNameInput
{
get { return _instructorNameInput; }
set
{
this.RaiseAndSetIfChanged(ref _instructorNameInput, value);
Submit.RaiseCanExecuteChanged();
}
}
However, the following isn't supported:
Submit.RaiseCanExecuteChanged();
As a result, how can I force bindings to update based on the CanExecute predicate that my command relies on?
Updated:
public partial class FormViewModel : ReactiveObject
{
public FormViewModel()
{
Submit = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanSubmit));
Submit.Subscribe(x => OnSubmit());
}
bool _canExecute;
public bool CanSubmit
{
get { return !GetUnsatisfied().Any(); }
set { this.RaiseAndSetIfChanged(ref _canExecute, value); } // Need to update view based on command.CanExecute state change
}
void OnSubmit()
{
var rosterInfo = new RosterInfo(new Course(CourseInput.Name),
new Instructor(InstructorNameInput, InstructorIdInput));
var repository = GetRepository();
repository.AddCourseInfo(rosterInfo);
Publish(REQUEST_NAVIGATION_TO_SUBMITION_CONFIRMATION, rosterInfo);
}
ObservableCollection<RequiredField> GetUnsatisfied()
{
RequiredFields.Clear();
RequiredFields = Review();
return RequiredFields;
}
}
Multiple issues:
Have a read at the fundamentals on ReactiveObject, in particular how "Read-Write Properties" are written.
In your case, this.WhenAnyValue(x => x.CanSubmit) will trigger a refresh on the command whenever the property CanSubmit changes, but this one never does, because you never call the setter (and the getter has an incorrect impl).
Currently, your method GetUnsatisfied() has "polling" semantics, which mean you need something to trigger this method to update your command. This isn't reactive at all, you should instead bind/listen to updates.
If there's no way for you to make your Review() logic reactive, then you may do something like:
var canExec = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.Select(_ => !GetUnsatisfied().Any());
Submit = ReactiveCommand.Create(canExec);
Submit.Subscribe(x => OnSubmit());
Basically, having a timer to do your polling.
(But I strongly suggest going further down the reactive way)
Related
When I click on an AjaxLink, I would like to have a validation via JavaScript on the client side first (because the LocalStorage is queried) and then depending on the result, further JavaScript calls are made. How can i achieve this?
In a pseudo code it would look like this:
new AjaxLink<>("myId", myModel) {
#Override
public void onClick(AjaxRequestTarget target) {
boolean isCounterValid = target.appendJavaScript(checkCounter()); // i know that this is not possible, therefore pseudo code
if(isCounterValid) {
target.appendJavaScript(someOtherJavaScript());
}
else {
target.appendJavaScript(anotherJavaScript());
}
}
private String checkCounter() {
return "var count = window.localStorage.getItem('myCounter'); return count !== 1;";
}
private String someOtherJavaScript() {
return "change something";
}
private String anotherJavaScript() {
return "change other thing";
}
};
You need to send extra request parameters with the Ajax call when the link is clicked. For that you should override updateAjaxAttributes(AjaxRequestAttributes attributes) method of AjaxLink:
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
attributes.getDynamicExtraParameters().add("var count = window.localStorage.getItem('myCounter'); return [{\"name\":\"count\", \"value\": count}]");
}
This way inside AjaxLink#onClick() you can read the count via:
int count = getRequest().getRequestParameters().getParameterValue("count").toInt();
AJAX components and behaviors can customize AJAX attributes overriding updateAjaxAttributes and using a custom implementation of AjaxCallListener which exposes different method to hook into the AJAX request cycle. In you case you could use AjaxCallListener#getBeforeSendHandler.
For a full introduction to this topic (with examples) see user guide:
https://ci.apache.org/projects/wicket/guide/8.x/single.html#_ajax_request_attributes_and_call_listeners
I currently have a view model looking like this:
public class PhrasesFrameViewModel : ObservableProperty
{
bool customPointsSwitch;
public PhrasesFrameViewModel()
{
var aButtonClickedCommand = new Command(() =>
{
App.DB.IncrementScore(App.cfs, App.phrase, (int)App.aBtn);
App.correctButtonPressed = (int)App.aBtn;
ResetTimer2();
});
var wordGridClickedCommand = new Command(() =>
{
if (App.Timer1Running)
ResetTimer1();
else
ResetTimer2();
});
}
private static void ResetTimer1()
{
if (App.tokenSource1 != null)
{
App.Timer1Seconds = 0;
App.tokenSource1.Cancel();
}
}
private static void ResetTimer2()
{
if (App.tokenSource2 != null)
{
App.Timer2Seconds = 0;
App.tokenSource2.Cancel();
}
}
public bool CustomPointsSwitch
{
get
{
return customPointsSwitch;
}
set
{
if (value != customPointsSwitch)
{
customPointsSwitch = value;
NotifyPropertyChanged("CustomPointsSwitch");
App.DB.UpdateBoolSetting(Settings.Cp, customPointsSwitch);
}
}
}
I believe most view models would have code similar to that for CustomPointsSwitch but how about the code for the gesture recognizers and the commands plus the small method for reset (used by a few other methods in the view model). Does that all belong in the view model or should it be in another class?
Short answer: In this particular case, as per code shared in question, they belong in view model.
Long answer:
It depends. If your command handler needs to interact with UI - it should stay in view; If it is anything else, i.e. presentation or business logic - it should be defined in view model.
Commands are integral part of MVVM pattern - as they allow for decoupling of view model from view, which in turn makes it easier to unit test, maintain and extend. They are the recommended channel for communication between view and view-model (other than data-binding).
In most of the cases, the command interface is used when a user-interaction/event in view needs to trigger various actions in the view-model - and in these cases command is defined in view-model itself and exposed as a property.
I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.
Simple case:
public class Foo : ReactiveObject
{
public Foo()
{
this.ObservableForProperty(t => t.Bar, t => t).Subscribe(t =>
{
//Logic using previous and new value for Bar
}
}
private int _bar;
public int Bar
{
get { return _bar; }
set { this.RaiseAndSetIfChanged(ref _bar, value); }
}
}
In the ObservableForProperty subscription, only the new value of Bar is accessible (via t). We can call ObservableForProperty with true for the "beforeChange" parameter, and instead only have the previous value and not the new.
I know I can just plug my code logic in the property setter, but I'd like to keep the ObservableForProperty behavior (filter the first setter and when the value does not change). The property is also used in a XAML binding and require the propertyChanged trigger.
Anything I missed? How can I do that easily? Thanks
How about something like this:
public Foo()
{
this.WhenAnyValue(t => t.Bar)
.Buffer(2, 1)
.Select(buf => new { Previous = buf[0], Current = buf[1] })
.Subscribe(t => { //Logic using previous and new value for Bar });
}
Note that this will not trigger the subscription logic the first time you alter the Bar. In order to get that functionality, add .StartWith(this.Bar) before buffering.
This uses Buffer operator in overlapping mode (skip < count).
I am using Prism 2, trying to add four navigation buttons (First Record, Last Record, Previous Record, Next Record) in shell to be used by modules. I also want these buttons to be disable if active View/ViewModel does not provide these functions.
I tried using events but didn't know how to achieve my second goal regarding disabling buttons. It seems I need to check current active View/ViewModel to see if they subscribed the click event during View switch. But I think publisher should be unaware of subscriber...
Somehow I tried my own way. I create an IDocNavigation interface which has four method corresponding to my four buttons. At runtime I check modules' ViewModel if they implemented that interface or not, and change the ICommand on fly. Below is my code. I include one LastRecordCommand only:
public ShellViewModel(Views.Shell shell)
{
this.Shell = shell;
shell.DataContext = this;
shell.MainDocking.ActivePaneChanged += (s, e) =>
{
if (e.NewPane.Content is UserControl &&
((UserControl)e.NewPane.Content).DataContext is IDocumentNavigate)
{
IDocumentNavigate vm = ((UserControl)e.NewPane.Content).DataContext as IDocumentNavigate;
LastRecordCommand = new RelayCommand(x => vm.GotoLastRecord(), x => true);
}
else
{
LastRecordCommand = new RelayCommand(x => { }, x => false);
}
};
//...
I feel these are quite ugly. Creating an empty RelayCommand is also stupid. How can I improve ? or how can I achieve disabling command if event is more suitable in my case ?
You can make use of CompositeCommand in prism.
Define a globally available CompositeCommand
public static readonly CompositeCommand FirstRecord= new CompositeCommand(true);
Then in your your module view models
class Module1
{
public DelegateCommand Module1Firstrecord{ get; set; }
Module1()
{
Module1Firstrecord = new DelegateCommand(this.FirstRecord, CanExecute);
}
private void FirstRecord()
{
//do whatever you want
}
private bool CanExecute()
{
return true;
}
private void Module1_IsActiveChanged(object sender, EventArgs e)
{
//Find if your window is acive
// if it is active Module1Firstrecord.IsActive = true
//else false.
}
}
With IActiveAware you can handle the active window scenario easily. According to whether your active module have a handler for the command on not the buttons will enable/disable.