-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Time Value Component #36
Conversation
…er a correct date with the picker
The tests aren't working very well since my local tests rely on my local time zone and the CI operates on a different time zone so the values I check for are always wrong on one of the tests (either local tests or the CI test) |
I think using this library would be ideal since it combines a date picker and a time picker. Unfortunately it requires Angular 9. There is an old version for Angular 8 but it is no longer supported. |
Running the code on localhost works with no issue and returns the correct timestamp. The offset for us from UTC is +2 hours so an offset value of -120 is correct (value is in minutes and a positive offset returns a negative number). Running the unit test fails because for some reason the offset is -34. I have no idea where this seemingly random offset is coming from. From my understanding of time (which is a bit unclear now), except for a couple of timezones, namely some Australian timezones, every offset only deals with hours, not minutes or seconds. This adds to my confusion because in the tests I am setting the time value to "22:00" and it is returning a timestamp of "1776-07-04T21:25:52.000Z" when I should be expecting "1776-07-04T20:00:00.000Z" to account for the offset. Somehow the offset is also affecting the minutes and seconds. Update I've opened up a new issue for this #38 |
… changed the comment input to a text area
@@ -0,0 +1,3 @@ | |||
::ng-deep .parent-value-component .mat-form-field { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for your information: https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😣technology evolves too fast. I'll update my code. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
::ng-deep, /deep/ and >>> deprecation
The ::ng-deep pseudo-class selector also has a couple of aliases: >>> and /deep/, and all three are soon to be removed.The main reason for that is that this mechanism for piercing the style isolation sandbox around a component can potentially encourage bad styling practices.
The situation is still evolving, but right now, ::ng-deep can be used if needed for certain use cases.
https://blog.angular-university.io/angular-host-context/
From what I can find online, Angular has marked the ::ng-deep selector as deprecated but has not yet provided a new solution. They recommend to use ::ng-deep only if absolutely necessary so I shall use it sparingly.
…o remove as much deprecated styling as possible
@mdelez Thanks, I will look at this PR today. |
…mponent to the child component and cleaned up a lot of repeated code
…hey can be more easily tested
I've refactored a lot my code and I think it looks quite clean now. Namely I have moved all of the date conversion and logic to the child component and now the parent component is only concerned about a datetime string. I've also moved the date conversion logic into separate methods so that it is easier to test. Some minor things still left to do:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I hit the cancel button for the time part
I get:
TimeInputComponent.html:8 ERROR RangeError: Invalid time value
at Date.toISOString ()
at TimeInputComponent.userInputToTimestamp (knora-ui.js:1822)
at TimeInputComponent.get value [as value] (knora-ui.js:1736)
at TimeInputComponent._handleInput (knora-ui.js:1810)
at Object.eval [as handleEvent] (TimeInputComponent.html:8)
at handleEvent (core.js:43993)
at callWithDebugContext (core.js:45632)
at Object.debugHandleEvent [as handleEvent] (core.js:45247)
at dispatchEvent (core.js:29804)
at core.js:42925
Probably this is because a cancelled time is null
.
return (control: AbstractControl): { [key: string]: any } | null => { | ||
|
||
const invalid = initValue === control.value && | ||
control.value.split(':').pop().split(';')[0].match(CustomRegex.TIME_REGEX) == null && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think yo don't need to validate the time in the parent since you are doing that already in the child component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the best case overriding the standardValidatorFunc
becomes unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed error and removed overridden validator in bfec41b
this.dateFormControl = new FormControl(null); | ||
this.dateFormControl.setValidators([Validators.required]); | ||
|
||
this.timeFormControl = new FormControl({value: null, Validators: [Validators.required, this.timeValidator]}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can directly put Validators.pattern(CustomRegex.TIME_REGEX)
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in bfec41b
<mat-error *ngIf="form.controls.timeValue.hasError('valueNotChanged')"> | ||
Please change the value | ||
</mat-error> | ||
</mat-form-field> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl); | ||
|
||
this.dateFormControl = new FormControl(null); | ||
this.dateFormControl.setValidators([Validators.required]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use the more compact form like below to set the validator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in bfec41b
} | ||
|
||
// converts and returns a unix timestamp string as an array consisting of a GregorianCalendarDate and a string | ||
convertTimestampToDateTime(timestamp: string): { gcd: GregorianCalendarDate, time: string }[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make the return type an object with one member being time and the other the date?
What is { gcd: GregorianCalendarDate, time: string }[]
supposed to mean? An array of objects each having a member gcd
and time
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, returning an object would probably be better. Currently it's returning an array of objects because gcd
and time
are different types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in bfec41b
} | ||
|
||
// return converted Date obj as a string without the milliseconds | ||
userInputToTimestamp(userInput: any): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you give the argument userInput
a type annotation other than any
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably you can make it consistent with what you return from convertTimestampToDateTime
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 6392c37
|
||
// return converted Date obj as a string without the milliseconds | ||
userInputToTimestamp(userInput: any): string { | ||
let splitTime = userInput.time.split(":"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the user cancels the date, time seems to be an empty string:
{date: GregorianCalendarDate, time: ""}date: GregorianCalendarDate {calendarStart: CalendarDate, calendarEnd: CalendarDate, exactDate: true, jdnStart: 2458922, jdnEnd: 2458922, …}time: ""__proto__: Object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in bfec41b
} | ||
} | ||
|
||
describe('TimeInputComponent', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you made separate functions for time conversion userInputToTimestamp
and convertTimestampToDateTime
, could add some tests for these? Just test the functions themselves, without any HTML/Dome interaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests added in bfec41b
@mdelez If you need my help, just give me a call. |
|
||
const date = new GregorianCalendarDate(new CalendarPeriod(calendarDate, calendarDate)); | ||
|
||
let time = this.datePipe.transform(timestamp, "HH:mm"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use const
instead of let
. Please use single quotes for strings.
Or just use WebStorm .. ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in e78d4cc I really tried to only use single quotes, but some slipped through 😜
@Input() | ||
get value(): string | null { | ||
const userInput = new DateTime(this.form.value.date, this.form.value.time); | ||
if (userInput.date !== null && userInput.time !== null && userInput.time !== '') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should also check the time string against the regex here (to prevent an error in this.userInputToTimestamp
). If invalid, null
is returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or probably you can just see if the FormControl
for time is valid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 548005d
|
||
set value(timestamp: string | null) { | ||
if (timestamp !== null) { | ||
const dateTime = this.convertTimestampToDateTime(timestamp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be wrapped in a try-catch block. If it fails to parse the timevalue string, you can still init {date: null, time: null}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 19c3324
get value(): string | null { | ||
if (this.form.valid) { | ||
const userInput = new DateTime(this.form.value.date, this.form.value.time); | ||
return this.userInputToTimestamp(userInput); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you wrap this with a try-catch block too (just return null if there is an error)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 19c3324
userInputToTimestamp(userInput: DateTime): string { | ||
const splitTime = userInput.time.split(':'); | ||
const updateDate = new Date(userInput.date.toCalendarPeriod().periodStart.year, | ||
(userInput.date.toCalendarPeriod().periodStart.month - 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment about 0-based indexes for months
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 19c3324
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thx!
I think we still need to find out about the timezones, but there is #38 for this.
closes #16