2

Here's a screenshot from Google Calendar. How can I build this interface in Angular/Material (currently 4.4.4 and 2.0.0-beta.12, respectively)?

screenshot of Google Calendar Wizard for repeating events

Note: there are two features in particular that I'm finding challenging for Angular and Material.

  1. The entire form changes depending on the first selection box. "Repeat daily" counts days whereas "repeat weekly" offers checkboxes for specific days of the week. ("Repeat monthly" offers unique options as well.) I'd really prefer a modular solution, where I can break the mutually exclusive sub-forms into their own component/formGroup. Managing validation in the presence of ngIf's is also causing me headaches.

  2. The "Ends:" block is a combination radio button with numeric and date inputs for certain options. I'm not sure how to approximate that with angular+material, or how to set up the validation. I'd be content making "Ends" be a selection box too, but in that case I wouldn't want a separate component/formGroup for the 0 or 1 input fields that follow. So I can't completely reduce it to an instance of problem #1.

2
  • #1 sounds like it can be handled by simple ng-if or ng-show. Perhaps you can show some code? Commented Oct 14, 2017 at 8:59
  • 1
    We came up with a generic solution for that issue and you can find a proper answer here: stackoverflow.com/a/55042618/2398593 or our library here github.com/cloudnc/ngx-sub-form :) Commented Mar 7, 2019 at 11:23

1 Answer 1

2

I had to build this same component for an app, and though it was based on the OSX reminders scheduler, it is approximately the same.

It's probably best to just focus on the "end" selection since the same concept can be applied throughout the component. The end FormGroup is a control in a larger FormGroup, and it looks like this

end: this.fb.group({
  // default to 'never'. Can also be 'date' or 'count'
  selection: 'never',
  // default the date to one week from now
  date: [startOfDay(addDays(oneHourFromNow, 7)), Validators.required],
  // default to only repeating once
  count: [1, [Validators.required, CustomValidators.number(this.countLimits)]]
})

Obviously, if you just submit this form as-is, it will contain a value that doesn't make much sense. To counteract this, you need to enable and disable the date and count controls as needed. When an abstract control is disabled, it won't be including in the form value.

this.endGroup.get('selection').valueChanges
  .startWith(null)
  .subscribe(() => {
    // if the selection value is 'never',
    // disable both the 'date' and 'count' controls

    // if the selection value is 'date',
    // enable 'date' control and disable 'count' control

    // if the selection value is 'count',
    // enable 'count' control and disable 'date' control
  });

The template for the end selector looks kind of like this,

<mat-select formControlName="selection">...</mat-select>

<ng-container *ngIf="selectionControl.value === 'date'">
  <!-- input with datepicker -->
</ng-container>

<ng-container *ngIf="selectionControl.value === 'count'">
  <!-- input with type="number" -->
</ng-container>

This article by Todd Motto helped too in encapsulating the form groups into their own presentational components. I'm happy to explain more, but it is definitely the most complex component I've had to build, so it's a bit much to include in a single answer.

Sign up to request clarification or add additional context in comments.

3 Comments

This is very helpful. One thing that sticks out is that even in Todd Motto's example, the FormGroup(s) for the entire tree of components is initialized in the root component. It seems less encapsulated than I expected, but at least it puts me on the right track. Your technique about enable and disable AbstractControl seems also to be key. Gimme a day or two to digest all this & test before flagging as an accepted solution. Looks promising.
Here's a plunkr to show what I was able to do with these resources @WillHowell provided.
I'm so happy to hear it was helpful! Your plunker looks solid. I kind of have the same concerns with the Todd Motto example... for our component, you would set the start, repeat, and end values in one form, but if you wanted to do something more complicated than 'daily', 'weekly', or 'monthly', you could open up a popover that had a miniform. It created it's own standalone form group that would save just it's value back to the other form group when the popover was submitted. If you have the luxury of using embedded forms with a separate submit button, perhaps you can also use that approach.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.