Material Datepicker change month view event - angular-material2

is there any way how to detect that calendar has new month view is loaded, when "yellow buttons" (see screenshot) are used?

You can listen for click events of those buttons on the calendar using Renderer2.
Code from this blog post.
constructor( private renderer: Renderer2g) { }
ngAfterViewInit() {
let buttons = document.querySelectorAll('mat-calendar .mat-calendar-previous-button,' +
'mat-calendar .mat-calendar-next-button');
if (buttons) {
Array.from(buttons).forEach(button => {
this.renderer.listen(button, "click", () => {
console.log("Month changed");
});
})
}
}

You can just use your own header component with whatever buttons and events your want:
<mat-calendar [headerComponent]="headerComponent"></mat-calendar>
And declare a variable headerComponent in the .ts:
public headerComponent = MyCoolHeaderComponent;
You will have to provide your own UI and functionality for the header (in your MyCoolHeaderComponent class).

MatDatepicker component uses DateAdapter service to handle dates, and when you click on next or previous button, a handler runs on MatCalendarHeader component and this handler calls addCalendarMonths method on DateAdapter (if current view is month) so if you patch this function on DateAdapter service TEMPORARILY, you can do anything you like, for example:
in parent component
origAddCalendarMonths: any
constructor(#Optional() private _dateAdapter: DateAdapter<any>) { }
onOpenedStream() {
this.origAddCalendarMonths = this._dateAdapter.addCalendarMonths
this._dateAdapter.addCalendarMonths = (...args) => {
console.log('!!!')
// your logic
return this.origAddCalendarMonths.apply(this._dateAdapter, args)
}
}
onClosedStream() {
this._dateAdapter.addCalendarMonths = this.origAddCalendarMonths
}
ngOnDestroy() {
this.onClosedStream()
}
or if you use MatCalendar instead, you can provide some extended class from DateAdapter in parent component providers to provide that class as DateAdapter for all child of this component (NodeInjector)

Related

How to get Label view in ViewModel to set accessibility focus in xamarin forms

I have Label in view, I need that Label's view in my ViewModel. I am using Dependency Service to set focus on Controls for Accessibility service, DS requires view as a param.
This is my Label
<Label
AutomationProperties.IsInAccessibleTree="{Binding ShowNoResults}"
IsVisible="{Binding ShowNoResults}"
Text="{Binding ResultsHeader}"/>
I tried Command but Label doesn't support command. Below code also not working
var view = GetView() as HomeworkView;
I am getting view always null. How can I fix this?
I am not quite sure what are you trying to achieve, but you can't access the View elements from you view model.
If you want to do something with the control, you can use the messaging center to do it, here is an example
in your ViewModel
MessagingCenter.Send(this, "your message here");
then in your page, you need to subscribe to this message from that view model and do the desired action
MessagingCenter.Instance.Unsubscribe<ViewModelClassNamedel>(this, "your message here");
MessagingCenter.Instance.Subscribe<ViewModelClassName>(this, "your message here", (data) =>
{
this.youControlName.Focus();
});
More detail added to Mohammad's answer.
Message Center doc.
In your ViewModel (with class name "YourViewModel"):
// Here we use control name directly.
// OR could make an "enum" with a value for each control.
string controlName = ...;
MessagingCenter.Send<YourViewModel>(this, "focus", controlName);
then in your page, subscribe to this message and do the desired action
.xaml.cs:
protected override void OnAppearing() {
{
base.OnAppearing();
// Unsubscribe before Subscribe ensures you don't Subscribe twice, if the page is shown again.
MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");
MessagingCenter.Instance.Subscribe<YourViewModel>(this, "focus", (controlName) =>
{
View v = null;
switch (controlName) {
case "name1":
v = this.name1;
break;
case "name2":
v = this.name2;
break;
}
if (v != null) {
//v.Focus();
// Tell VM to use v as view.
((YourViewModel)BindingContext).SetFocus(v);
}
});
}
protected override void OnDisappearing() {
MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");
base.OnDisappearing();
}
If need to pass View v back to VM, because that has the logic to use it:
public class YourViewModel
{
public void SetFocus(View view)
{
... your code that needs label's view ...
}
}
Not tested. Might need some slight changes. Might need
...(this, "focus", (sender, controlName) =>
instead of
...(this, "focus", (controlName) =>
UPDATE
Simple approach, if there is only ONE View that is needed in VM.
public class YourViewModel
{
public View ViewToFocus { get; set; }
// The method that needs ViewToFocus.
void SomeMethod()
{
...
if (ViewToFocus != null)
... do something with it ...
}
}
public class YourView
{
public YourView()
{
InitializeComponent();
...
// After BindingContext is set.
((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
}
}
ALTERNATIVE: It might be cleaner/more robust to set ViewToFocus in page's OnAppearing, and clear it in OnDisappearing. This ensures it is never used while the page is not visible (or in some delayed action after the page has gone away). I would probably do it this way.
protected override void OnAppearing()
{
base.OnAppearing();
((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
}
protected override void OnDisappearing()
{
((YourViewModel)BindingContext).ViewToFocus = null;
base.OnDisappearing();
}

Button enableWhen TableView selectedItem is not empty

TableView selectedItem is bind to ViewModel and i want to button be disabled when selectedItem is null and enabled otherwise.
class MainView: View("TheSubberKt") {
override val root = Form()
val model = MainViewModel()
init {
with(root) {
fieldset {
field("Media:") {
textfield(model.mediaPath)
button("...")
}
}
tableview(subs) {
// ...
bindSelected(model.selectedSubtitle)
}
hbox {
button("Hash Search")
button("Download Selected") {
// what to do here?
}
}
}
}
}
I imagine that i have to create a OvservableValue<Boolean> and then pass to enableWhen but, how to do that?
Is it possible to map a property onChange to a custom observable? (just another idea)
Observable values have a function that will return an BooleanBinding when they don't contain a value, and since you've already bound the selected item to model.selectedSubtitle, you can simply add the following expression inside of the button builder:
enableWhen(model.selectedSubtitle.isNotNull)
If you didn't bind the selected item of the TableView to a property accessible in the view, you could store a reference to the table directly and bind to state in the table's selection model:
enableWhen(table.selectionModel.selectedItemProperty().isNotNull)
On another note, you can clean up your syntax by getting rid of the init block and declare the root node directly with a builder:
override val root = tableview(subs) {
...
}
Hope this helps :)

Where do you put your gesture commands in Xamarin? In the View Model?

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.

Angular Material 2: How to refresh md-table after editing a record?

So I have a user-list component and a user-detail component.
The user-list component has an md-table listing all users registered. The user is able to click on a button to see the details of the corresponding entity.
After editing the Name property and saving (for example), the user-detail redirects to the user-list component. But the md-table displays the old information.
How can I trigger an md-table refresh after editing the entity in another component like this scenario?
Here is my user-list component:
export class UserListComponent implements OnInit {
tableDisplayedColumns: string[] = ['userName', 'email', 'options'];
userDataSource: UserDataSource;
constructor(private _userService: UserService, private _router: Router) { }
ngOnInit(): void {
this.userDataSource = new UserDataSource(this._userService);
}}
class UserDataSource extends DataSource<UserVModel> {
constructor(private _userService: UserService) {
super();
}
connect(): Observable<UserVModel[]> {
return this._userService.getAllUsers();
}
disconnect(): void { }}
The table will re-render when the stream provided by connect() emits a new value.
getAllUsers needs to emit a new set of data when it is changed. Otherwise, listen for a separate stream (e.g. dataChanged) from the _userService and use that to call getAllUsers again.
connect(): Observable<UserVModel[]> {
return this._userService.dataChanged
.switchMap(() => this._userService.getAllUsers()
}
Actually the problem was that the User-Detail component was redirecting before the observable had the chance to complete. So I placed the router.navigate as a complete function.
Code Before
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err));
this._router.navigate(['/user']);
}
Code After
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err), () => this.afterSubmit());
}
afterSubmit() {
this._router.navigate(['/user']);
}
Prior to this change, I was getting the old values after the redirect. Using the complete function, I get up to date values without the need to use a dataChanged observable in the service.
You could tidy things up and put your router navigation inside the response :
onSubmit() {
this._updateSub = this._service.updateUser(this._id,
this._userForm.value).subscribe(
res => this._router.navigate(['/user']);
err => this.onSubmitError(err), () => this.afterSubmit());
}

Prism Shell buttons shared by modules

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.

Resources