Skip to content

Commit

Permalink
feat(primeng/p-badge): rework dynamic property rerender for directive
Browse files Browse the repository at this point in the history
  • Loading branch information
volvachev committed Apr 26, 2023
1 parent 38d4df3 commit 0abff48
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 88 deletions.
184 changes: 104 additions & 80 deletions src/app/components/badge/badge.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { CommonModule, DOCUMENT } from '@angular/common';
import { AfterViewInit, ChangeDetectionStrategy, Component, Directive, ElementRef, Inject, Input, NgModule, OnDestroy, Renderer2, ViewEncapsulation } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, Component, Directive, ElementRef, Inject, Input, NgModule, Renderer2, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { SharedModule } from 'primeng/api';
import { DomHandler } from 'primeng/dom';
import { UniqueComponentId } from 'primeng/utils';

type BadgeDirectiveIconPosition = 'left' | 'right' | 'top' | 'bottom';
type BadgeSize = 'large' | 'xlarge';

@Directive({
Expand All @@ -13,103 +12,83 @@ type BadgeSize = 'large' | 'xlarge';
class: 'p-element'
}
})
export class BadgeDirective implements AfterViewInit, OnDestroy {
@Input() iconPos: BadgeDirectiveIconPosition = 'left';
export class BadgeDirective implements OnChanges, AfterViewInit {
@Input('badgeDisabled') public disabled: boolean;

@Input('badgeDisabled') get disabled(): boolean {
return this._disabled;
}
set disabled(val: boolean) {
this._disabled = val;
}

@Input() public get size(): BadgeSize {
return this._size;
}
set size(val: BadgeSize) {
this._size = val;

if (this.initialized) {
this.setSizeClasses();
}
}

public _value: string;

public initialized: boolean;
@Input('badgeSize') public size: BadgeSize;

private id: string;

private _disabled: boolean = false;
private get activeElement(): HTMLElement {
return this.el.nativeElement.nodeName.indexOf('-') != -1 ? this.el.nativeElement.firstChild : this.el.nativeElement;
}

private _size: BadgeSize;
private get canUpdateBadge(): boolean {
return this.id && !this.disabled;
}

constructor(@Inject(DOCUMENT) private document: Document, public el: ElementRef, private renderer: Renderer2) {}

ngAfterViewInit() {
this.id = UniqueComponentId() + '_badge';
let el = this.el.nativeElement.nodeName.indexOf('-') != -1 ? this.el.nativeElement.firstChild : this.el.nativeElement;

if (this._disabled) {
return null;
public ngOnChanges({ value, size, severity, disabled }: SimpleChanges): void {
if (disabled) {
this.toggleDisableState();
}

let badge = this.document.createElement('span');
badge.id = this.id;
badge.className = 'p-badge p-component';

if (this.severity) {
DomHandler.addClass(badge, 'p-badge-' + this.severity);
if (!this.canUpdateBadge) {
return;
}

this.setSizeClasses(badge);

if (this.value != null) {
this.renderer.appendChild(badge, this.document.createTextNode(this.value));

if (String(this.value).length === 1) {
DomHandler.addClass(badge, 'p-badge-no-gutter');
}
} else {
DomHandler.addClass(badge, 'p-badge-dot');
if (severity) {
this.setSeverity(severity.previousValue);
}

DomHandler.addClass(el, 'p-overlay-badge');
this.renderer.appendChild(el, badge);
if (size) {
this.setSizeClasses();
}

this.initialized = true;
if (value) {
this.setValue();
}
}

@Input() get value(): string {
return this._value;
public ngAfterViewInit(): void {
this.id = UniqueComponentId() + '_badge';
this.renderBadgeContent();
}

set value(val: string) {
if (val !== this._value) {
this._value = val;
@Input() public value: string | number;

if (this.initialized) {
let badge = document.getElementById(this.id);
@Input() public severity: string;

if (this._value) {
if (DomHandler.hasClass(badge, 'p-badge-dot')) DomHandler.removeClass(badge, 'p-badge-dot');
private setValue(element?: HTMLElement): void {
const badge = element ?? this.document.getElementById(this.id);

if (String(this._value).length === 1) {
DomHandler.addClass(badge, 'p-badge-no-gutter');
} else {
DomHandler.removeClass(badge, 'p-badge-no-gutter');
}
} else if (!this._value && !DomHandler.hasClass(badge, 'p-badge-dot')) {
DomHandler.addClass(badge, 'p-badge-dot');
}
if (!badge) {
return;
}

badge.innerHTML = '';
this.renderer.appendChild(badge, document.createTextNode(this._value));
if (this.value != null) {
if (DomHandler.hasClass(badge, 'p-badge-dot')) {
DomHandler.removeClass(badge, 'p-badge-dot');
}

if (this.value && String(this.value).length === 1) {
DomHandler.addClass(badge, 'p-badge-no-gutter');
} else {
DomHandler.removeClass(badge, 'p-badge-no-gutter');
}
} else {
if (!DomHandler.hasClass(badge, 'p-badge-dot')) {
DomHandler.addClass(badge, 'p-badge-dot');
}

DomHandler.removeClass(badge, 'p-badge-no-gutter');
}
}

@Input() severity: string;
badge.innerHTML = '';
const badgeValue = this.value != null ? String(this.value) : '';
this.renderer.appendChild(badge, this.document.createTextNode(badgeValue));
}

private setSizeClasses(element?: HTMLElement): void {
const badge = element ?? this.document.getElementById(this.id);
Expand All @@ -118,13 +97,13 @@ export class BadgeDirective implements AfterViewInit, OnDestroy {
return;
}

if (this._size) {
if (this._size === 'large') {
if (this.size) {
if (this.size === 'large') {
DomHandler.addClass(badge, 'p-badge-lg');
DomHandler.removeClass(badge, 'p-badge-xl');
}

if (this._size === 'xlarge') {
if (this.size === 'xlarge') {
DomHandler.addClass(badge, 'p-badge-xl');
DomHandler.removeClass(badge, 'p-badge-lg');
}
Expand All @@ -134,8 +113,53 @@ export class BadgeDirective implements AfterViewInit, OnDestroy {
}
}

ngOnDestroy() {
this.initialized = false;
private renderBadgeContent(): void {
if (this.disabled) {
return null;
}

const el = this.activeElement;
const badge = this.document.createElement('span');
badge.id = this.id;
badge.className = 'p-badge p-component';

this.setSeverity(null, badge);
this.setSizeClasses(badge);
this.setValue(badge);
DomHandler.addClass(el, 'p-overlay-badge');
this.renderer.appendChild(el, badge);
}

private setSeverity(oldSeverity?: string, element?: HTMLElement): void {
const badge = element ?? this.document.getElementById(this.id);

if (!badge) {
return;
}

if (this.severity) {
DomHandler.addClass(badge, `p-badge-${this.severity}`);
}

if (oldSeverity) {
DomHandler.removeClass(badge, `p-badge-${oldSeverity}`);
}
}

private toggleDisableState(): void {
if (!this.id) {
return;
}

if (this.disabled) {
const badge = this.activeElement?.querySelector(`#${this.id}`);

if (badge) {
this.renderer.removeChild(this.activeElement, badge);
}
} else {
this.renderBadgeContent();
}
}
}

Expand All @@ -154,11 +178,11 @@ export class Badge {

@Input() style: any;

@Input() size: BadgeSize;
@Input('badgeSize') size: BadgeSize;

@Input() severity: string;

@Input() value: string;
@Input() value: string | number;

@Input() badgeDisabled: boolean = false;

Expand Down
2 changes: 1 addition & 1 deletion src/app/components/fileupload/fileupload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ export class FileUpload implements AfterViewInit, AfterContentInit, OnInit, OnDe

ngOnDestroy() {
if (this.content && this.content.nativeElement) {
if(this.dragOverListener) {
if (this.dragOverListener) {
this.dragOverListener();
this.dragOverListener = null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/showcase/doc/badge/propsdoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Component, Input } from '@angular/core';
<td>Severity type of the badge.</td>
</tr>
<tr>
<td>size</td>
<td>badgeSize</td>
<td>string</td>
<td>null</td>
<td>Size of the badge, valid options are "large" and "xlarge".</td>
Expand Down
12 changes: 6 additions & 6 deletions src/app/showcase/doc/badge/sizedoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { Code } from '../../domain/code';
selector: 'badge-size-demo',
template: ` <section>
<app-docsectiontext [title]="title" [id]="id">
<p>Badge sizes are adjusted with the <i>size</i> property that accepts <i>large</i> and <i>xlarge</i> as the possible alternatives to the default size. Currently sizes only apply to component mode.</p>
<p>Badge sizes are adjusted with the <i>badgeSize</i> property that accepts <i>large</i> and <i>xlarge</i> as the possible alternatives to the default size. Currently sizes only apply to component mode.</p>
</app-docsectiontext>
<div class="card flex justify-content-center">
<p-badge value="2"></p-badge>
<p-badge value="4" size="large" severity="warning"></p-badge>
<p-badge value="6" size="xlarge" severity="success"></p-badge>
<p-badge value="4" badgeSize="large" severity="warning"></p-badge>
<p-badge value="6" badgeSize="xlarge" severity="success"></p-badge>
</div>
<app-code [code]="code" selector="badge-size-demo"></app-code>
</section>`
Expand All @@ -22,12 +22,12 @@ export class SizeDoc {

code: Code = {
basic: `
<p-badge value="4" size="large" severity="warning"></p-badge>`,
<p-badge value="4" badgeSize="large" severity="warning"></p-badge>`,
html: `
<div class="card flex justify-content-center">
<p-badge value="2"></p-badge>
<p-badge value="4" size="large" severity="warning"></p-badge>
<p-badge value="6" size="xlarge" severity="success"></p-badge>
<p-badge value="4" badgeSize="large" severity="warning"></p-badge>
<p-badge value="6" badgeSize="xlarge" severity="success"></p-badge>
</div>`,
typescript: `
import { Component } from '@angular/core';
Expand Down

0 comments on commit 0abff48

Please sign in to comment.