Skip to content

refactor: replace color getters and setters with mixin #4974

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

Merged

Conversation

devversion
Copy link
Member

  • Replaces the color getters and setters with a Mixin function (similar as for the disabled property)
  • This should reduce payload and also should make it way easier to maintain the color functionality.

Now it would be cool to say the Payload Diff github status 😉

@devversion devversion requested a review from jelbourn June 5, 2017 16:13
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Jun 5, 2017
const AVAILABLE_COLOR_VALUES = ['primary', 'accent', 'warn'];

/** @docs-private */
export interface IsColorable {
Copy link
Member

Choose a reason for hiding this comment

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

CanColor (I want to consistently start all mixins with Can)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds a bit weird, but makes sense to have it consistent

}

/** @docs-private */
export interface ColorableBase {
Copy link
Member

Choose a reason for hiding this comment

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

I'd call this HasRenderer

Copy link
Member Author

Choose a reason for hiding this comment

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

Should this still live in the color.ts file then?


get color(): string { return this._color; }
set color(value: string) {
if (AVAILABLE_COLOR_VALUES.indexOf(value) !== -1 || (allowNoColor && !value)) {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than having a run-time check, use string literal values for the type so that it's enforced at compile time:

protected _color: 'primary' | 'accent' | 'warn' | undefined = undefined;

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea with the types for TypeScript. Although I wanted to do the runtime check because people can still set the color to whatever they want and the class would be added like mat-test.

But yeah maybe this doesn't really matter since we enforce it using TypeScript. What do you think?

}

/** Method that changes the color classes on the host element. */
private _setColorClass(colorName: string, isAdd: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this method is really necessary; the setter can directly do

set color(value: string) {
  this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
  this._renderer.addClass(this._elementRef.nativeElement, `mat-${value}`);
  this._color = value;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

This doesn't work properly. Imagine someone sets the value to null. A class mat-null would be added.

For sure you can place the check if the class truthy inside of the getter too, but it feels better to do it like that.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I forgot the check; still think you can do it in the setter directly, though

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah true, but I mean it's just easier to read like that.

Copy link
Member

Choose a reason for hiding this comment

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

The main reason I don't like it is the isAdd argument:
https://martinfowler.com/bliki/FlagArgument.html

So my thinking is "How do we avoid the boolean argument? Well, it would just be two different methods. Oh, well we already have that on renderer, so just use that."

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree that this might be an argument to move this to the setter itself but I kinda don't like having another level of nesting here:

set color(value: ThemePalette) {
  if (value || allowNoColor && !value) {
    if (this._color) {
       this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
     }
     if (value) {
       this._renderer.addClass(this._elementRef.nativeElement, `mat-${value}`);
     }
     this._color = value;
   }
}

Any other ideas for this? I'm willing to change it though.

Copy link
Member

Choose a reason for hiding this comment

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

That seems fine to me; sometimes that's just how the logic goes.


// Boilerplate for applying mixins to MdChip.
export class MdPseudoCheckboxBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
Copy link
Member

Choose a reason for hiding this comment

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

Should do this with properties rather than a constructor because the constructor approach will generate more code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I had to go back to this approach because just setting the properties inside of TypeScript means that the superclass cannot set the default color on initialization (maybe a trade-off)

}

/** Method that changes the color classes on the host element. */
private _setColorClass(colorName: string, isAdd: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

That seems fine to me; sometimes that's just how the logic goes.


get color(): ThemePalette { return this._color; }
set color(value: ThemePalette) {
if (value || allowNoColor && !value) {
Copy link
Member

Choose a reason for hiding this comment

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

What is allowNoColor? I didn't notice that before

Copy link
Member Author

Choose a reason for hiding this comment

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

Some components allow having no color. E.g buttons don't have by default any color of primary, accent and warn.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I see it more as being some components require a color. Can we make it so that the argument is instead an optional defaultColor?

Copy link
Member Author

@devversion devversion Jun 5, 2017

Choose a reason for hiding this comment

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

You mean that we can pass a value that is the default value to the mixin? (e.g null)

I'd rather do it the other way around because the default value "should" be basically everything that is falsy.

And if colors are required and someone sets it to a fasly value it should not change at all.

Copy link
Member

Choose a reason for hiding this comment

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

I mean

export function mixinColor<T extends Constructor<HasRenderer>>(base: T, defaultColor?: ThemePalette)
    : Constructor<CanColor> & T {
  return class extends base {
    private _color: ThemePalette = defaultColor;

    get color(): ThemePalette { return this._color; }
    set color(value: ThemePalette) {
      const newPalette = value || defaultColor;
      this._updatePaletteClasses(newPalette);
      this._color = newPalette;
    }
    
    constructor(...args: any[]) { 
      super(...args);
      if (defaultColor) {
        this._addPaletteClass(defaultColor);
      } 
    }
    
    private _updatePaletteClasses(newPalette: ThemePalette) {
      if (newPalette != this.color) {
        this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
        this._renderer.addClass(this._elementRef.nativeElement, `mat-${newPalette}`);
      }
    }
  };
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah I think I now know what you mean. This seems to be a good idea.

Copy link
Member Author

Choose a reason for hiding this comment

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

Made this change.

* Replaces the color getters and setters with a Mixin function (similar as for the disabled property)
* This should reduce payload and also should make it way easier to maintain the color functionality.
@devversion devversion force-pushed the refactor/switch-to-color-mixin branch from fa11306 to 9c5465b Compare June 6, 2017 12:18
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

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

LGTM

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed pr: needs review labels Jun 6, 2017
@andrewseguin andrewseguin merged commit 3779395 into angular:master Jun 7, 2017
@devversion devversion deleted the refactor/switch-to-color-mixin branch June 7, 2017 21:59
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants