Angular powered Bootstrap UI component library with comprehensive Bootstrap 5 components for Angular applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Enhanced form controls including rating, timepicker, and typeahead components with full Angular forms integration and accessibility support.
import {
NgbRatingModule,
NgbTimepickerModule,
NgbTypeaheadModule
} from '@ng-bootstrap/ng-bootstrap';Star rating component with customizable appearance and behavior.
@Component({
selector: 'ngb-rating',
exportAs: 'ngbRating'
})
class NgbRating implements ControlValueAccessor {
/** Maximum rating value */
@Input() max: number;
/** Current rating value */
@Input() rate: number;
/** If true, rating is readonly */
@Input() readonly: boolean;
/** If true, allows resetting to 0 by clicking current rate */
@Input() resettable: boolean;
/** Template for custom star appearance */
@Input() starTemplate: TemplateRef<StarTemplateContext>;
/** Event emitted when hovering over a star */
@Output() hover: EventEmitter<number>;
/** Event emitted when mouse leaves the rating */
@Output() leave: EventEmitter<number>;
/** Event emitted when rating changes */
@Output() rateChange: EventEmitter<number>;
/** Reset rating to 0 */
reset(): void;
/** Update rating value */
update(value: number): void;
}Time picker component for hour, minute, and second selection.
@Component({
selector: 'ngb-timepicker',
exportAs: 'ngbTimepicker'
})
class NgbTimepicker implements ControlValueAccessor {
/** If true, display 12-hour format with AM/PM */
@Input() meridian: boolean;
/** If true, show spinner buttons */
@Input() spinners: boolean;
/** If true, show seconds input */
@Input() seconds: boolean;
/** Hour step for spinner buttons */
@Input() hourStep: number;
/** Minute step for spinner buttons */
@Input() minuteStep: number;
/** Second step for spinner buttons */
@Input() secondStep: number;
/** If true, inputs are readonly */
@Input() readonlyInputs: boolean;
/** Size of the timepicker */
@Input() size: 'small' | 'medium' | 'large';
/** Navigate to specific time */
navigateTime(step: number): void;
}Autocomplete/typeahead directive providing powerful search functionality for input elements.
@Directive({
selector: 'input[ngbTypeahead]',
exportAs: 'ngbTypeahead'
})
class NgbTypeahead implements ControlValueAccessor {
/** Autocomplete attribute for the input */
@Input() autocomplete: string;
/** Container element for the dropdown */
@Input() container: string;
/** If false, selected value cannot be edited */
@Input() editable: boolean;
/** If true, first item will be focused automatically */
@Input() focusFirst: boolean;
/** Function to format selected item in input field */
@Input() inputFormatter: (item: any) => string;
/** Operator function that provides search suggestions */
@Input() ngbTypeahead: OperatorFunction<string, readonly any[]> | null | undefined;
/** Function to format items in result list */
@Input() resultFormatter: (item: any) => string;
/** Custom template for result items */
@Input() resultTemplate: TemplateRef<ResultTemplateContext>;
/** If true, selects item on exact match */
@Input() selectOnExact: boolean;
/** If true, shows hint with first match */
@Input() showHint: boolean;
/** Preferred placement of dropdown */
@Input() placement: PlacementArray;
/** Popper options modifier function */
@Input() popperOptions: (options: Partial<Options>) => Partial<Options>;
/** CSS class for dropdown popup */
@Input() popupClass: string;
/** Event emitted when item is selected */
@Output() selectItem: EventEmitter<NgbTypeaheadSelectItemEvent>;
/** Active descendant for accessibility */
activeDescendant: string | null;
/** Popup window ID */
popupId: string;
/** Dismiss the typeahead popup */
dismissPopup(): void;
/** Check if popup is open */
isPopupOpen(): boolean;
}Component for highlighting search terms within text results.
@Component({
selector: 'ngb-highlight',
exportAs: 'ngbHighlight'
})
class NgbHighlight {
/** CSS class for highlighted spans (default: 'ngb-highlight') */
@Input() highlightClass: string;
/** Text to add highlighting to (required) */
@Input({ required: true }) result?: string | null;
/** Term or terms to highlight (required) */
@Input({ required: true }) term: string | readonly string[];
/** If true, highlighting is accent-sensitive */
@Input() accentSensitive: boolean;
/** Array of text parts after processing */
parts: string[];
}@Injectable({ providedIn: 'root' })
class NgbRatingConfig {
/** Default maximum rating */
max: number;
/** Default readonly state */
readonly: boolean;
/** Default resettable state */
resettable: boolean;
}
@Injectable({ providedIn: 'root' })
class NgbTimepickerConfig {
/** Default meridian setting */
meridian: boolean;
/** Default spinners setting */
spinners: boolean;
/** Default seconds setting */
seconds: boolean;
/** Default hour step */
hourStep: number;
/** Default minute step */
minuteStep: number;
/** Default second step */
secondStep: number;
/** Default disabled state */
disabled: boolean;
/** Default readonly inputs state */
readonlyInputs: boolean;
/** Default size */
size: 'small' | 'medium' | 'large';
}
@Injectable({ providedIn: 'root' })
class NgbTypeaheadConfig {
/** Default container */
container: string;
/** Default editor formatter */
editable: boolean;
/** Default focus first */
focusFirst: boolean;
/** Default show hint */
showHint: boolean;
/** Default placement */
placement: PlacementArray;
}interface NgbTimeStruct {
hour: number;
minute: number;
second: number;
}
interface NgbTypeaheadSelectItemEvent {
/** Selected item */
item: any;
/** Prevent default selection behavior */
preventDefault: () => void;
}
interface StarTemplateContext {
/** Star value/index */
$implicit: number;
/** Current fill percentage (0-100) */
fill: number;
/** Star index */
index: number;
}
interface ResultTemplateContext {
/** Search result item */
$implicit: any;
/** Search term */
term: string;
/** Formatter function */
formatter: (item: any) => string;
}@Component({
template: `
<div class="mb-3">
<label>Rate this item:</label>
<ngb-rating
[(rate)]="currentRate"
[max]="5"
[readonly]="false"
[resettable]="true"
(rateChange)="onRateChange($event)">
</ngb-rating>
<p>Current rating: {{ currentRate }}</p>
</div>
`
})
export class BasicRatingComponent {
currentRate = 3;
onRateChange(rate: number) {
console.log('Rating changed to:', rate);
}
}@Component({
template: `
<ngb-rating
[(rate)]="currentRate"
[starTemplate]="customStar">
</ngb-rating>
<ng-template #customStar let-fill="fill" let-index="index">
<span class="custom-star" [class.filled]="fill === 100">
<i class="bi" [class.bi-heart-fill]="fill === 100" [class.bi-heart]="fill < 100"></i>
</span>
</ng-template>
`
})
export class CustomStarRatingComponent {
currentRate = 2;
}@Component({
template: `
<div class="mb-3">
<label>Select time:</label>
<ngb-timepicker
[(ngModel)]="selectedTime"
[meridian]="true"
[seconds]="true"
[spinners]="true">
</ngb-timepicker>
<p *ngIf="selectedTime">Selected: {{ selectedTime | json }}</p>
</div>
`
})
export class BasicTimepickerComponent {
selectedTime: NgbTimeStruct = { hour: 13, minute: 30, second: 0 };
}@Component({
template: `
<form [formGroup]="timeForm" (ngSubmit)="onSubmit()">
<div class="mb-3">
<label>Meeting time:</label>
<ngb-timepicker
formControlName="meetingTime"
[meridian]="false"
[seconds]="false">
</ngb-timepicker>
</div>
<div class="mb-3">
<label>Rating:</label>
<ngb-rating
formControlName="rating"
[max]="10">
</ngb-rating>
</div>
<button type="submit" class="btn btn-primary" [disabled]="timeForm.invalid">
Submit
</button>
</form>
`
})
export class ReactiveFormComponent implements OnInit {
timeForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.timeForm = this.fb.group({
meetingTime: [{ hour: 14, minute: 0, second: 0 }, Validators.required],
rating: [5, [Validators.required, Validators.min(1)]]
});
}
onSubmit() {
if (this.timeForm.valid) {
console.log('Form data:', this.timeForm.value);
}
}
}@Component({
template: `
<div class="mb-3">
<label for="typeahead-basic">Search for a state:</label>
<input
id="typeahead-basic"
type="text"
class="form-control"
[(ngModel)]="selectedState"
[ngbTypeahead]="search"
placeholder="Type to search...">
</div>
<p *ngIf="selectedState">Selected: {{ selectedState }}</p>
`
})
export class BasicTypeaheadComponent {
selectedState: string = '';
states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California'];
search = (text$: Observable<string>) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map(term => term.length < 2 ? []
: this.states.filter(v => v.toLowerCase().indexOf(term.toLowerCase()) > -1).slice(0, 10))
);
}@Component({
template: `
<div class="mb-3">
<label for="typeahead-template">Search users:</label>
<input
id="typeahead-template"
type="text"
class="form-control"
[(ngModel)]="selectedUser"
[ngbTypeahead]="searchUsers"
[resultTemplate]="userTemplate"
[inputFormatter]="userFormatter"
(selectItem)="onUserSelect($event)">
</div>
<ng-template #userTemplate let-user="result" let-term="term">
<div class="d-flex align-items-center">
<img [src]="user.avatar" class="rounded-circle me-2" width="32" height="32">
<div>
<div><ngb-highlight [result]="user.name" [term]="term"></ngb-highlight></div>
<small class="text-muted">{{ user.email }}</small>
</div>
</div>
</ng-template>
`
})
export class AdvancedTypeaheadComponent {
selectedUser: any;
users = [
{ id: 1, name: 'John Doe', email: 'john@example.com', avatar: 'avatar1.jpg' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', avatar: 'avatar2.jpg' }
];
searchUsers = (text$: Observable<string>) =>
text$.pipe(
debounceTime(300),
distinctUntilChanged(),
map(term => term.length < 2 ? []
: this.users.filter(user =>
user.name.toLowerCase().includes(term.toLowerCase()) ||
user.email.toLowerCase().includes(term.toLowerCase())
).slice(0, 10))
);
userFormatter = (user: any) => user.name;
onUserSelect(event: NgbTypeaheadSelectItemEvent) {
console.log('User selected:', event.item);
}
}