Skip to content

Commit

Permalink
1. Tempate type added - 'inline' | 'popup', Default 'popup'
Browse files Browse the repository at this point in the history
2. Now only able to pass options using [NgPasswordValidator] to minimize complexity.
3. Performance optimizations
  • Loading branch information
Jagan Mohan Bishoyi committed Jul 9, 2022
1 parent 45ccee8 commit ea442b1
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
</div>

<div class="content" role="main">
<input type="text" id="password" name="password" placeholder="Password.." [NgPasswordValidator]="options">
<input type="text" id="password" name="password" placeholder="Password.." [NgPasswordValidator]="options"
(valid)="isValid($event)">
<input type="text" name="password" placeholder="Password..">
<input type="text" name="password" placeholder="Password..">
<input type="text" name="password" placeholder="Password..">
<input type="text" name="password" placeholder="Password..">
<!-- <input type="text" id="password" name="password" placeholder="Password.." NgPasswordValidator> -->
</div>

Expand Down
17 changes: 15 additions & 2 deletions projects/ng-password-validator-dev/src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,34 @@ p {

.content {
display: flex;
margin: 82px auto 32px;
margin: 150px auto 32px;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;

input {
width: 400px;
margin-top: 60px;
font-size: max(16px, 1em);
font-family: inherit;
padding: 0.25em 0.5em;
background-color: #fff;
border: 2px solid #eeeeee;
border-radius: 4px;
height: 28px;
margin-bottom: 15px;
}
}

::ng-deep {
.custom-class {
.popup-window {
padding: 0;

.heading {
color: #9c0404;
font-family: inherit;
}
}
}
}
9 changes: 8 additions & 1 deletion projects/ng-password-validator-dev/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { NgPasswordValidatorOptions } from 'projects/ng-password-validator/src/lib/ng-password-validator.interface';

@Component({
selector: 'app-root',
Expand All @@ -7,8 +8,10 @@ import { Component } from '@angular/core';
})
export class AppComponent {
title = 'ng-password-validator-dev';
options = {
options: NgPasswordValidatorOptions = {
placement: 'bottom',
type: 'inline',
'custom-class': 'custom-class',
rules: {
password: {
type: 'range',
Expand All @@ -19,4 +22,8 @@ export class AppComponent {
shadow: true,
offset: 15,
};

isValid(event: boolean): void {
console.log(event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
.popup-window {
.heading {
font-size: $heading-font-size;
color: $black;
margin-bottom: 0.5rem;
font-weight: 700;
}
Expand Down Expand Up @@ -97,6 +96,4 @@
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,9 @@ export class NgPasswordValidatorComponent implements OnInit, OnChanges {
setStyles(): void {
this.setZIndex();
this.setAnimationDuration();

this.hostClassShadow = this.options['shadow'];
if (this.options.type !== 'inline') {
this.hostClassShadow = this.options['shadow'];
}
this.hostStyleMaxWidth = this.options['max-width'] + 'px';
this.hostStyleWidth = this.options['width']
? this.options['width'] + 'px'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,14 @@ import { Subscription } from 'rxjs';
import { DataService } from './data.service';
import { NgPasswordValidatorComponent } from './ng-password-validator.component';
import {
HostComponent,
IElementPosition,
IPosition,
NgPasswordValidatorOptions,
} from './ng-password-validator.interface';
import { NgPasswordValidatorService } from './ng-password-validator.service';
import { defaultOptions } from './options';

export interface HostComponent {
data: any;
show: boolean;
close: boolean;
events: any;
}
import { UtilsService } from './utils.service';

@Directive({
selector: '[NgPasswordValidator]',
Expand All @@ -52,25 +47,7 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
passwordOptions: NgPasswordValidatorOptions;
componentSubscribe: Subscription;

@Input('options') set optionsInput(value: NgPasswordValidatorOptions) {
if (value && defaultOptions) {
this.passwordOptions = this.deepMerge(defaultOptions, value);
this.createPasswordRegex();
}
}
@Input('NgPasswordValidator') popup: NgPasswordValidatorOptions;
@Input('placement') placement: string;
@Input('z-index') zIndex: number;
@Input('animation-duration') animationDuration: number;
@Input('custom-class') customClass: string;
@Input('shadow') shadow: boolean;
@Input('theme') theme: 'basic' | 'pro';
@Input('offset') offset: number;
@Input('width') width: number;
@Input('max-width') maxWidth: number;
@Input('position') position: IPosition;
@Input('heading') heading: string;
@Input('successMessage') successMessage: string;

@Output() events: EventEmitter<any> = new EventEmitter<any>();
@Output() valid: EventEmitter<boolean> = new EventEmitter();
Expand All @@ -81,6 +58,7 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private dataService: DataService,
private utilsService: UtilsService,
private injector: Injector
) {}

Expand All @@ -92,13 +70,6 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
return this.componentRef && this.componentRef.hostView.destroyed;
}

/**
* Get popup position
*
* @readonly
* @type {(IElementPosition | IPosition)}
* @memberof NgPasswordValidatorDirective
*/
get popupPosition(): IElementPosition | IPosition {
if (this.options['position']) {
return this.options['position'];
Expand All @@ -118,19 +89,6 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
this.show();
this.checkPassword(value);
}
/**
* Update password options
*
* @memberof NgPasswordValidatorDirective
*/
updatePasswordOptions(): void {
if (this.popup && defaultOptions) {
this.passwordOptions = this.deepMerge(defaultOptions, this.popup);
} else {
this.passwordOptions = { ...defaultOptions };
}
this.createPasswordRegex();
}

/**
* Focus out of input field
Expand All @@ -139,7 +97,10 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
*/
@HostListener('focusout')
onMouseLeave(): void {
this.destroyPopup();
// If the template type is inline, don't destroy the created template
if (this.passwordOptions.type !== 'inline') {
this.destroyPopup();
}
this.valid.emit(this.isValid);
}

Expand All @@ -161,6 +122,12 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
* @memberof NgPasswordValidatorDirective
*/
ngOnChanges(changes: { popup: SimpleChange }): void {
// If the template type is 'inline' create the inline template directly
const templateType = changes.popup.currentValue.type;
if (templateType === 'inline') {
this.updatePasswordOptions();
this.show();
}
const changedOptions = this.getProperties(changes);
this.applyOptionsDefault(changedOptions, defaultOptions);
}
Expand All @@ -177,31 +144,6 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
}
}

/**
* Deep merge objects
*
* @param {NgPasswordValidatorOptions} target
* @param {NgPasswordValidatorOptions} source
* @returns {NgPasswordValidatorOptions}
* @memberof NgPasswordValidatorDirective
*/
deepMerge(
target: NgPasswordValidatorOptions,
source: NgPasswordValidatorOptions
): NgPasswordValidatorOptions {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (const key of Object.keys(source)) {
if (source[key] instanceof Object) {
Object.assign(source[key], this.deepMerge(target[key], source[key]));
}
}

// Join `target` and modified `source`
Object.assign(target || {}, source);

return target;
}

/**
* Create password regex
*
Expand Down Expand Up @@ -273,6 +215,23 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
this.dataService.updateValue(data);
}

/**
* Update password options
*
* @memberof NgPasswordValidatorDirective
*/
updatePasswordOptions(): void {
if (this.popup && defaultOptions) {
this.passwordOptions = this.utilsService.deepMerge(
defaultOptions,
this.popup
);
} else {
this.passwordOptions = { ...defaultOptions };
}
this.createPasswordRegex();
}

/**
* Get properties
*
Expand Down Expand Up @@ -400,6 +359,11 @@ export class NgPasswordValidatorDirective implements OnDestroy, OnChanges {
).events.subscribe((event: any) => {
this.handleEvents(event);
});

if (this.options.type === 'inline') {
this.elementRef.nativeElement.style.marginBottom =
this.popupPosition['bottom'] + 'px';
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface NgPasswordValidatorOptions {
'custom-class'?: string;
shadow?: boolean;
theme?: 'basic' | 'pro';
type?: 'inline' | 'popup';
offset?: number;
width?: number;
'max-width'?: number;
Expand Down Expand Up @@ -48,3 +49,9 @@ export interface IStatus {
'include-lowercase-characters': boolean;
'include-uppercase-characters': boolean;
}
export interface HostComponent {
data: any;
show: boolean;
close: boolean;
events: any;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { NgPasswordValidatorComponent } from './ng-password-validator.component'
import { NgPasswordValidatorDirective } from './ng-password-validator.directive';
import { NgPasswordValidatorOptions } from './ng-password-validator.interface';
import { NgPasswordValidatorService } from './ng-password-validator.service';
import { UtilsService } from './utils.service';

@NgModule({
declarations: [NgPasswordValidatorDirective, NgPasswordValidatorComponent],
imports: [CommonModule],
providers: [DataService],
providers: [DataService, UtilsService],
exports: [NgPasswordValidatorDirective],
entryComponents: [NgPasswordValidatorComponent],
})
Expand Down
1 change: 1 addition & 0 deletions projects/ng-password-validator/src/lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const defaultOptions: NgPasswordValidatorOptions = {
'custom-class': '',
shadow: true,
theme: 'pro',
type: 'popup',
offset: 8,
heading: 'Password Policy',
successMessage: 'Awesome! Password requirement fulfilled.',
Expand Down
30 changes: 30 additions & 0 deletions projects/ng-password-validator/src/lib/utils.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { NgPasswordValidatorOptions } from './ng-password-validator.interface';

@Injectable()
export class UtilsService {
/**
* Deep merge objects
*
* @param {NgPasswordValidatorOptions} target
* @param {NgPasswordValidatorOptions} source
* @returns {NgPasswordValidatorOptions}
* @memberof UtilsService
*/
deepMerge(
target: NgPasswordValidatorOptions,
source: NgPasswordValidatorOptions
): NgPasswordValidatorOptions {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (const key of Object.keys(source)) {
if (source[key] instanceof Object) {
Object.assign(source[key], this.deepMerge(target[key], source[key]));
}
}

// Join `target` and modified `source`
Object.assign(target || {}, source);

return target;
}
}

0 comments on commit ea442b1

Please sign in to comment.