0

I'm trying to validate that a string-input is a valid CSS color.

So, I've got a custom validator in my Angular App (based on some tutorials and some validation suggestions found on SO):

import { AbstractControl, ValidationErrors } from '@angular/forms';

export class GamePlatformValidator {

    static mustBeValidColor(control: AbstractControl): ValidationErrors | null {
        const ele = document.createElement('div');
        ele.style.color = control.value as string;
        const color = ele.style.color.split(/\s+/).join('').toLowerCase();

        if (color === '') {
            ele.remove();
            return { mustBeValidColor: true };
        }
        return null;
    }
}

I understand what's happening in the code, and the above code validates the string just fine, but it clearly can't be used as is since a hard-refresh of the page would require the document to be ready for this code to work.

Unfortunately, I have no idea how to fix this, as I'm fairly new to angular and practical javascript in general.

Any suggestions would be most appreciated.

Updated with Implementation

'platform.component.ts' (partial)

import { PlatformValidator } from './platform.validator';

export class GamesPlatformsComponent implements OnInit {
    public form = new FormGroup({
        platform: new FormGroup({
            id: new FormControl(),
            name: new FormControl('', [Validators.required, Validators.minLength(2)]),
            icon: new FormControl('', [Validators.required, GamePlatformValidator.startsWithDot]),
            color: new FormControl('' GamePlatformValidator.mustBeValidColor),
            hidden: new FormControl(false)
        })
    });
}

'platform.component.html' (partial)

<div class="input-group">
    <span class="input-group-addon">Color</span>
    <input formControlName="color"
            id="color" type="text"
            class="form-control"
            placeholder="Enter #color-code..." />
</div>
<div *ngIf="color.invalid && color.touched" class="alert alert-danger top-margin">
    <div *ngIf="color.errors.mustBeValidColor">
        <p>Must be a valid css color.</p>
        <hr />
        <h6>Examples:</h6>
        <ul>
            <li>red</li>
            <li>#f00</li>
            <li>#ff0000</li>
            <li>rgb(255,0,0)</li>
            <li>rgba(255,0,0,1)</li>
        </ul>
    </div>
</div>

To clarify: The validator works fine when I don't browse to the form page directly. So, if the apps is able to load by navigating to any other url, the validation will work fine. However, if I browse directly to the page with the form, 'document will not be defined' yet since it's still being loaded from the server.

nGAGE
  • 177
  • 1
  • 11
  • How do you implement this validator? – Vega Aug 27 '17 at 15:34
  • @Vega Sorry, didn't think that code was necessary. Added relevant code snippets. – nGAGE Aug 27 '17 at 16:01
  • Despite the fact that you're getting the `document is not defined`, I think you should consider taking another approach to check if it's a valid hexadecimal code. Take a look [**in this question**](https://stackoverflow.com/questions/8027423/how-to-check-if-a-string-is-a-valid-hex-color-representation). You can easily adopt any of the answers to your validator. – developer033 Aug 27 '17 at 17:06
  • @developer033 I've seen that answer and originally used this approach, but checking for #value isn't enough. I want to account for rgb() rgba() and color-names as well. – nGAGE Aug 27 '17 at 17:38

1 Answers1

0

So, I took @developer033's comment to heart and decided to go for a different approach. Instead of using a validator, I decided to simply create a method to check the validity upon (blur) and set the error manually.

Perhaps in its current form, not as reusable as a validator, but that can be improved upon.

The result:

platform.component.ts (partial)

import { GamePlatformValidator } from './platform.validator';

export class GamesPlatformsComponent implements OnInit {
    public platforms: any[];
    public form = new FormGroup({
        platform: new FormGroup({
            id: new FormControl(),
            name: new FormControl('', [Validators.required, Validators.minLength(2)]),
            icon: new FormControl('', [Validators.required, GamePlatformValidator.startsWithDot]),
            color: new FormControl(''),
            hidden: new FormControl(false)
        })
    });

    ...

    get color() {
        return this.form.get('platform.color');
    }

    ...

    validateColor() {
        const ele = document.createElement('div');
        ele.style.color = this.color.value;
        const color = ele.style.color.split(/\s+/).join('').toLowerCase();

        if (color === '') {
            this.color.setErrors({ invalidColor: true });
        } else {
            this.color.setErrors(null);
        }
        ele.remove();
    }
}

platform.component.html (partial)

<div class="input-group">
    <span class="input-group-addon">Color</span>
    <input formControlName="color"
            id="color" type="text"
            class="form-control"
            placeholder="Enter #color-code..."
            (blur)="validateColor()" />
</div>
<div *ngIf="color.invalid && color.touched" class="alert alert-danger top-margin">
    <div *ngIf="color.errors.invalidColor">
        <p>Must be a valid css color.</p>
        <hr />
        <h6>Examples:</h6>
        <ul>
            <li>red</li>
            <li>#f00</li>
            <li>#ff0000</li>
            <li>rgb(255,0,0)</li>
            <li>rgba(255,0,0,1)</li>
        </ul>
    </div>
</div>
nGAGE
  • 177
  • 1
  • 11