Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

refactor(datepicker) #1599

Closed
wants to merge 1 commit into from
Closed

refactor(datepicker) #1599

wants to merge 1 commit into from

Conversation

bekos
Copy link
Contributor

@bekos bekos commented Jan 17, 2014

I will start a PR thread for input/comments, while I am refactoring the datepicker directive.
Main goal is:

  • Split into smaller directives (dayPicker, monthPicker, yearPicker and be easy to add more)
  • Move common logic shared by directives into controller.
  • Address issues with min, max, initial selection, repeated dates etc (TODO: reference #issues)

currentText: scope.currentText || datepickerPopupConfig.currentText,
toggleWeeksText: scope.toggleWeeksText || datepickerPopupConfig.toggleWeeksText,
clearText: scope.clearText || datepickerPopupConfig.clearText,
closeText: scope.closeText || datepickerPopupConfig.closeText
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was playing around with this and I realized an alternative maybe to use getter methods for these scope variables that need to fallback to default values if undefined.

E.g.
getCloseText: function () { return scope.closeText || datepickerPopupConfig.closeText }

Of course, they can be shortened and generalized if they're the same keys on the scope and config objects.

Just a suggestion / thought.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Is there an issue for this in AngularJS or is it the expected behavior?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. not sure what you mean. I was referring to the switch from $observe to this scope.template.
This current change breaks the binding of the currentText (perhaps that's intended).
However, if you do want to bind it, one solution is to use a getter method to fetch it from the scope (in the template, it would be a function call like {{ template.getCloseText() }} ... or {{ get('closeText') }} if we use a generic getter ).

The getter method can enforce the defaults. This way, if the scope variable is changed to undefined, it still falls back to the datepickerPopupConfig value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to break the binding. No need to obsereve these changes in the default behavior.
What I mean is that last night I tried:

scope.closeText  = scope.closeText || datepickerPopupConfig.closeText;

in the tempate the {{closeText}} was evaluated again from the attribute, ie null, didn't get the default value.
That's why I assign in a different variable.

I hope this makes sense :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, that null thing is a bit of a pain. A way around it is perhaps to use evalAsync but I think using a getter function would also solve that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but this doesn't seem to be like the expected behavior, that's why I'm asking if you know if there is an issue for this in AngularJS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is expected behavior because using scope attributes (scope: { closeText: '@' , .. }) just adds watchers (with $observe) to the attributes. The watchers initial run is done asynchronously (similar to evalAsync, I think), so they have not run yet at this point.

I don't know if there's an open issue on this and I'm about to head to sleep so I won't be able to dig it out for you heh.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem :-)

@Foxandxss
Copy link
Contributor

I like the changes so far. Datepicker is a monster, both test a code.

@Foxandxss
Copy link
Contributor

I guess this is the best place (better than a new issue) to discuss an idea I had in mind.

Typically the calendars / datepickers are 7x6 and this one goes between 4 and 6 rows. I think that it would be better if we fix it to 6 rows. That would help designers I guess, to not have a jumpy element when moving through months. On the other hand, someone could like to be able to see some of the past/next month days.

I see this behavior in practically any calendar, windows, mac, mobile...

Since the date generator algorithm is that awesome (I am stealing it to make my app better, sorry), it is just changing a couple of numbers to make this change.

Not sure what's the opinion of the community :)

@bekos
Copy link
Contributor Author

bekos commented Jan 25, 2014

The refactor is almost done. It is still missing some tests, mainly for the new features, documentation changes and other small tweaks, but the main logic is already there. I will leave it here for some days in case someone wants to make a code review.

A few key-points off the top of my head.

Breaking changes:

  • show-weeks is not $watched.
  • *-format attributes have been renamed to format-*
  • min/max have been renamed min-date/max-date

Features:

  • added datepicker-mode as optional two-way binded attribute. Use: initialize datepicker in specific mode and watch/set mode outside of the datepicker
  • added init-date to specify initial view when model is not set
  • added visual hint for current day, month and year.

Fixes:

  • Repeated dates issue

Internals:

  • Split into separate directives with unique templates. This also helped in better separation of concerns.

TODO (in following PRs):

  • use ng-if for popup directive that will increase performance significantly.
  • use ngAnimate for transitions between modes and opening/closing (with help by @chrisirhc :-) )

@bekos
Copy link
Contributor Author

bekos commented Jan 25, 2014

@Foxandxss I hear what you say and I agree that this is probably a better experience, as you say. I think it's an easy fix. You can open a PR after this one has landed :-)

@Foxandxss
Copy link
Contributor

It should be easy to fix and test. I'll check your changes shortly :)

@tomchentw
Copy link
Contributor

@Foxandxss +1 for fix it to 6 rows :)

@Foxandxss
Copy link
Contributor

@bekos I have some thoughts:

Isn't selected a bad name choice? People could get confused with the ngModel date and selected date. I would rename it to selectedCalendar or calendarDate (something like that). It would help reading the code and understanding it.

Otoh I have a question myself: Why is the ngModel put on a parent? It is a weird usage imho. Normally you only need to put your directive, but having to wrap it on an element with a ngModel is weird. What were the needs to do that?

@bekos
Copy link
Contributor Author

bekos commented Jan 30, 2014

@Foxandxss I agree about the selected, I will rename to something more understandable.

The parent was a workaround before 1.2, and the issue with isolated scopes (angular/angular.js@909cabd). I have changed the demo now.

this._refreshView();

var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
ngModelCtrl.$setValidity('date-disabled', !date || (this.mode && !this.isDisabled(date)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are supporting an optional ngModel, this line seems to cry when we don't have any ngModel assigned. Probably there are more spots. Maybe we need a battery of tests for when there is no ngModel? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. Let me land this and open a PR later for the optional ng-model fixes.

@Foxandxss
Copy link
Contributor

I think I have some kind of love with this directive :P

Have you thought about putting all the pieces in different files, that would ease the hacking and maybe opening a door for new features.

I got a guy at IRC who wanted a multi day datepicker. Even when the idea was discarded (no enough knowledge to hack it himself) that gave me the idea. We could split it in different files so someone can just fork daypicker.js to add some changes and when we update, he just need to maintain one small file instead of a big big one.

On the other hand that could open a door to add a multidaypicker.js some day or other kind of datepicker related directives.

 * Add two-way binded `datepicker-mode`.
 * Add optional `init-date` when no model value is specified.
 * Add hint for current date.
 * Use isolated scope for popup directive.
 * Use optional binding for `isOpen`.
 * Split each mode into it's own directive.

BREAKING CHANGE:
`show-weeks` is no longer a watched attribute
`*-format` attributes have been renamed to `format-*`
`min` attribute has been renamed to `min-date`
`max` attribute has been renamed to `max-date`
@bekos bekos closed this in 7f4b40e Feb 8, 2014
@Foxandxss
Copy link
Contributor

Uhh, merged. What does remain to do here @bekos ? The non-model fix and the 42 days calendar, right?

I will work on the 42 days calendar soon and PR it to discuss it.

@bekos
Copy link
Contributor Author

bekos commented Feb 9, 2014

Other things TODO:

  • use ng-if for popup directive that will increase performance significantly when multiple instances exist on the same page.
  • use ngAnimate for transitions between modes and opening/closing
  • add keyboard navigation
  • fix to 7 weeks per month (I am not sure if this should be an option)
  • fix Datepicker today button sets time #1726
  • fix missing ngModel scenario

(ping @tomchentw)

@Foxandxss
Copy link
Contributor

There is a weird behavior that I am not sure how to fix. When in a given month you pick on a date of the next month, depending on the next month layout, you will get that new date selected but the button you used to select is still focused.

@bekos
Copy link
Contributor Author

bekos commented Feb 9, 2014

@Foxandxss Thx for noticing. The issue was the wrong track by expression (#1771)

benjaminch pushed a commit to benjaminch/bootstrap that referenced this pull request Apr 15, 2014
Also, change the way disabled dropdownToggle is read from `attrs` instead of element property.

Closes angular-ui#1867
Closes angular-ui#1870

Add an optionnal parameter on timepicker object in order to control the visibility of the controls arrows.

chore(tests): Add unit tests for arrows control visibility

Fixing demo semi colon issue.

chore(release): Add unit tests

chore(timepicker): Add LOC to check the user's input

chore(timepicker): Remove  method for showArrowControls option

chore(timepicker): Remove Show / Hide option from demo

chore(timepicker): Hides <tr> element rather than <td> element.

chore(tests): Simplify arrows control visibility tests

chore(tests): Refactoring arrow visibility tests

refactor(rating): use `track by` in template

Closes angular-ui#1724

refactor(pagination): add `href` and `track by` in templates

Closes angular-ui#1725

fix(progressbar): allow fractional values for bar width

 * Fractions are limited to two decimals, and are not included in aria values.

Closes angular-ui#1761

fix(dropdown): use $animate for adding and removing classes

Closes angular-ui#1644

feat(datepicker): add `datepicker-mode`, `init-date` & today hint

 * Add two-way binded `datepicker-mode`.
 * Add optional `init-date` when no model value is specified.
 * Add hint for current date.
 * Use isolated scope for popup directive.
 * Use optional binding for `isOpen`.
 * Split each mode into it's own directive.

Closes angular-ui#1599

BREAKING CHANGE:
`show-weeks` is no longer a watched attribute
`*-format` attributes have been renamed to `format-*`
`min` attribute has been renamed to `min-date`
`max` attribute has been renamed to `max-date`

refactor(rating): evaluate attributes inside init function

Closes angular-ui#1590
Closes angular-ui#1768

refactor(pagination): remove unused watch

Closes angular-ui#1769

fix(typeahead): correctly higlight numeric matches

Fixes angular-ui#1777

refactor(typeahead): use `ng-if` and `track by` in template

Closes angular-ui#1722

demo(typeahead): correct demo for asyn results

Fixes angular-ui#1740

refactor(datepicker): track buttons by date instead of $index

refactor(pagination): move boundary & directions to the template

Closes angular-ui#795
Closes angular-ui#1770

feat(alert): add WAI-ARIA markup

Closes angular-ui#1806

chore(demo): fix fork button width

Closes angular-ui#1805

fix(datepicker): `Today` button should not set time

Fixes angular-ui#1726
Closes angular-ui#1808

fix(progressbar): number filter in bar template and only for percent

Closes angular-ui#1807

refactor(dropdown): remove isolated scope

Closes angular-ui#1818

feat(typeahead): add WAI-ARIA markup

Closes angular-ui#1814

refactor(accordion): transclude function in compile is deprecated.

Closes angular-ui#1789

refactor(carousel): use `track by` in template

Closes angular-ui#1723

fix(datepicker): mark input field as invalid if the date is invalid

Closes angular-ui#1845

Conflicts:
	src/datepicker/datepicker.js
	src/datepicker/test/datepicker.spec.js
benjaminch pushed a commit to benjaminch/bootstrap that referenced this pull request May 10, 2014
Also, change the way disabled dropdownToggle is read from `attrs` instead of element property.

Closes angular-ui#1867
Closes angular-ui#1870

Add an optionnal parameter on timepicker object in order to control the visibility of the controls arrows.

chore(tests): Add unit tests for arrows control visibility

Fixing demo semi colon issue.

chore(release): Add unit tests

chore(timepicker): Add LOC to check the user's input

chore(timepicker): Remove  method for showArrowControls option

chore(timepicker): Remove Show / Hide option from demo

chore(timepicker): Hides <tr> element rather than <td> element.

chore(tests): Simplify arrows control visibility tests

chore(tests): Refactoring arrow visibility tests

fix(dropdown): unbind toggle element event on scope destroy

Also, change the way disabled dropdownToggle is read from `attrs` instead of element property.

Closes angular-ui#1867
Closes angular-ui#1870

Add an optionnal parameter on timepicker object in order to control the visibility of the controls arrows.

chore(tests): Add unit tests for arrows control visibility

Fixing demo semi colon issue.

chore(release): Add unit tests

chore(timepicker): Add LOC to check the user's input

chore(timepicker): Remove  method for showArrowControls option

chore(timepicker): Remove Show / Hide option from demo

chore(timepicker): Hides <tr> element rather than <td> element.

chore(tests): Simplify arrows control visibility tests

chore(tests): Refactoring arrow visibility tests

refactor(rating): use `track by` in template

Closes angular-ui#1724

refactor(pagination): add `href` and `track by` in templates

Closes angular-ui#1725

fix(progressbar): allow fractional values for bar width

 * Fractions are limited to two decimals, and are not included in aria values.

Closes angular-ui#1761

fix(dropdown): use $animate for adding and removing classes

Closes angular-ui#1644

feat(datepicker): add `datepicker-mode`, `init-date` & today hint

 * Add two-way binded `datepicker-mode`.
 * Add optional `init-date` when no model value is specified.
 * Add hint for current date.
 * Use isolated scope for popup directive.
 * Use optional binding for `isOpen`.
 * Split each mode into it's own directive.

Closes angular-ui#1599

BREAKING CHANGE:
`show-weeks` is no longer a watched attribute
`*-format` attributes have been renamed to `format-*`
`min` attribute has been renamed to `min-date`
`max` attribute has been renamed to `max-date`

refactor(rating): evaluate attributes inside init function

Closes angular-ui#1590
Closes angular-ui#1768

refactor(pagination): remove unused watch

Closes angular-ui#1769

fix(typeahead): correctly higlight numeric matches

Fixes angular-ui#1777

refactor(typeahead): use `ng-if` and `track by` in template

Closes angular-ui#1722

demo(typeahead): correct demo for asyn results

Fixes angular-ui#1740

refactor(datepicker): track buttons by date instead of $index

refactor(pagination): move boundary & directions to the template

Closes angular-ui#795
Closes angular-ui#1770

feat(alert): add WAI-ARIA markup

Closes angular-ui#1806

chore(demo): fix fork button width

Closes angular-ui#1805

fix(datepicker): `Today` button should not set time

Fixes angular-ui#1726
Closes angular-ui#1808

fix(progressbar): number filter in bar template and only for percent

Closes angular-ui#1807

refactor(dropdown): remove isolated scope

Closes angular-ui#1818

feat(typeahead): add WAI-ARIA markup

Closes angular-ui#1814

refactor(accordion): transclude function in compile is deprecated.

Closes angular-ui#1789

refactor(carousel): use `track by` in template

Closes angular-ui#1723

fix(datepicker): mark input field as invalid if the date is invalid

Closes angular-ui#1845

Conflicts:
	src/datepicker/datepicker.js
	src/datepicker/test/datepicker.spec.js
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants