josemarluedke / frontile

Component Library for Ember Octane apps
https://frontile.dev
MIT License
55 stars 21 forks source link

<Form.Input @type="datetime-local" /> woes #84

Open MichalBryxi opened 4 years ago

MichalBryxi commented 4 years ago

This really goes to the weirdness of how datetime-local works, but maybe internal wiring to make it work out-of the box would be a good idea.

The problem

Following code will not work:

// models/meeting.js
import Model, { attr } from '@ember-data/model';

export default class MeetingModel extends Model {
  @attr('date') startedAt;
}
...
<Form.Input
  @fieldName="startedAt"
  @type="datetime-local"
/>
...

The problem is that respective HTML element will return value as string that has format as follows: 2017-06-01T08:30. Which then does not get translated properly from/to Date() used in ember-data. Also I believe this will make problems with validation.

Proposed solution

Workaround

In case anyone wonders, there is pretty simple manual solution for this:

// models/meeting.js
import Model, { attr } from '@ember-data/model';
import moment from "moment";

export default class MeetingModel extends Model {
  @attr('date') startedAt;

  get startedAtString() {
    return moment(this.startedAt).format('YYYY-MM-DDTHH:mm');
  }

  set startedAtString(newDate) {
    this.startedAt = new Date(newDate);
  }
}
...
<Form.Input
  @fieldName="startedAtString"
  @type="datetime-local"
/>
...
josemarluedke commented 3 years ago

This is interesting. I'm not sure if it makes sense to handle that automatically. It really depends on how you are handling the data. For example, if you're using Ember Data, it might make sense, but if you are not, then this could be a bad assumption to transform the data into a Date object.

For these cases where we want to handle the data differently than the default browser behavior, I would not use the Changeset From components, instead, I would use FormInput directly where I can control the onChange, onInput etc.

Does this make sense?

MichalBryxi commented 3 years ago

This one is old. Hope I remember all my thoughts.

I would not use the Changeset From components, instead, I would use FormInput directly where I can control the onChange, onInput etc

This sounds like you would lose the changeset validation though? If not, could you give an example that would work?

if you're using Ember Data, it might make sense...

Yes, I do agree with that one. I wanted to figure out whether there is some one-size-fits all solution. Or whether this might be a note in documentation to help the newcomers?

MichalBryxi commented 1 year ago

And for my future self a version without using moment:

@attr('date') issuedAt;

get issuedAtString() {
  return this.issuedAt.toISOString().replace(/:\d+.\d+Z$/, '');
}

set issuedAtString(newDate) {
  this.issuedAt = new Date(`${newDate}:00.000Z`);
}

Apparently even according to MDN there is no nice way to convert Date() to datetime-local friendly format:

There are several methods provided by JavaScript's Date that can be used to convert numeric date information into a properly-formatted string. For example, the Date.toISOString() method returns the date/time in UTC with the suffix "Z" denoting that timezone; removing the "Z" would provide a value in the format expected by a datetime-local input.

And in reverse MDN warns against different browsers trying to parse strings into dates with different levels of success:

Note: When parsing date strings with the Date constructor (and Date.parse, they are equivalent), always make sure that the input conforms to the ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ) — the parsing behavior with other formats is implementation-defined and may not work across all browsers. A library can help if many different formats are to be accommodated.