UNPKG

ngx-tagify

Version:

Angular library that wraps @yaireo/tagify

329 lines (245 loc) 9.4 kB
# ngx-tagify Proper Angular library that wraps [@yaireo/tagify](https://github.com/yairEO/tagify). It allows multiple instances of tagify, implements ControlValueAccessor (for use with `ngModel` and reactive forms), and includes proper type declarations. __[Demo](https://brakebein.github.io/ngx-tagify/)__ * [Install](#install) * [Component](#component) * [Usage with ngModel](#usage-with-ngmodel) * [Usage with Reactive Forms](#usage-with-reactive-forms) * [Predefined values](#predefined-values) * [Inputs](#inputs) * [Outputs](#outputs) * [Service](#service) * [Styling](#styling) * [FAQ](#faq) ## Install Install via npm: npm install ngx-tagify Import module: ```typescript import { TagifyModule } from 'ngx-tagify'; @NgModule({ imports: [ ... TagifyModule, ... ] }) export class AppModule {} ``` Include styling (see [below](#styling)). ## Component You can use the `<tagify>` component either with `ngModel` or with reactive forms. Either way, it takes a `string` or an array of `TagData`, i.e. an `Object` that contains a unique property `value`: ```typescript interface TagData { value: string; [key: string]: any; } ``` If a string is passed, it gets parsed for tags by Tagify. The returned string is the stringified tags array or, if `mode: 'mix'`, the mixed text and tags string. ### Usage with `ngModel` Import `FormsModule` to your module. ```html <tagify [(ngModel)]="tags" inputClass="form-control" [settings]="settings" [whitelist]="whitelist$" [readonly]="readonly" [disabled]="disabled" (add)="onAdd($event)" (remove)="onRemove($event)" ></tagify> ``` ```typescript import { Component } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { TagData, TagifySettings } from 'ngx-tagify'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { tags: TagData[] = [{ value: 'foo' }]; // tags = 'foo'; -> if you want to pass as string settings: TagifySettings = { placeholder: 'Start typing...', blacklist: ['fucking', 'shit'], callbacks: { click: (e) => { console.log(e.detail); } } }; whitelist$ = new BehaviorSubject<string[]>(['hello', 'world']); readonly = false; disabled = false; onAdd(tagify) { console.log('added a tag', tagify); } onRemove(tags) { console.log('removed a tag', tags); } } ``` __Note:__ The component only recognizes reference changes, it won't deep check for changes within the array. `this.tags.push({value: 'bar'});` won't do anything. Instead, use `this.tags = this.tags.concat([{value: 'bar'}]);` (or similar) to update changes. ### Usage with Reactive Forms Import `ReactiveFormsModule` to your module. ```html <form [formGroup]="form"> <tagify formControlName="tags"></tagify> </form> ``` ```typescript import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { form = new FormGroup({ tags: new FormControl([{ value: 'Reactive' }]) }); ngOnInit(): void { this.form.valueChanges.subscribe(value => { console.log('form value changed', value); }); } } ``` ### Predefined values Use `ngModel` or reactive forms with an initial string value that gets parsed by Tagify. If you want to pass predefined tags as text, but receive a tags array as output, pass the value as text between `<tagify></tagify>`. Mixed text & tags are also supported. ```html <tagify>tag 1, tag 2</tagify> ``` ```html <tagify [settings]="{ mode: 'mix' }"> [[Eric Cartman]] and [[kyle]] do not know [[homer simpson]] because he's a relic. </tagify> ``` Angular has problems with hard-coded single curly braces. Use property binding to add predefined tags with json syntax. ```typescript originalText = '[[{"id":200, "value":"cartman", "title":"Eric Cartman"}]] and [[kyle]] do not know [[{"value":"homer simpson", "readonly":true}]] because he\'s a relic.'; ``` ```html <tagify [settings]="{ mode: 'mix' }"> {{ originalText }} </tagify> ``` ### Inputs | <!-- --> | <!-- --> | |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `settings` | _Type:_ `TagifySettings`<br>See [tagify/Settings](https://github.com/yairEO/tagify#settings). | | `inputClass` | _Type:_ `string`<br>Apply one or more CSS classes to the input field (e.g. Bootstrap's `form-control`). | | `whitelist` | _Type:_ `Observable<string[]\|TagData[]>`<br>Execution of the observable updates the whitelist of tagify. You can listen to user's inputs and update the whitelist respectively using this observable. | | `readonly` | _Type:_ `boolean`<br>Dynamically change readonly status. | | `disabled` | _Type:_ `boolean`<br>Dynamically change disabled status. | | `name` | _Type:_ `string`<br>Use the name attribute if you want to access the tagify component via the [service](#service). This name should be unique. | ### Outputs | <!-- --> | <!-- --> | |---|---| |`add`|Fires when a tag has been added.| |`remove`|Fires when a tag has been removed.| |`tInput`|Listen to the `input` event of the tagify input element.| Listen to all other events by defining respective callbacks ([tagify/Events](https://github.com/yairEO/tagify#events)). ## Service You can also gain access to the full [tagify API](https://github.com/yairEO/tagify#methods) via a service. Provide a `name`, such that the tagify instance will be available via the service. ```html <tagify name="example"></tagify> <button (click)="addTags()">Add tags</button> ``` ```typescript import { Component } from '@angular/core'; import { TagifyService } from 'ngx-tagify'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor( private tagifyService: TagifyService ) {} addTags() { this.tagifyService.get('example').addTags(['this', 'is', 'cool']); } } ``` The original Tagify class is also exposed and can be used for type declarations or custom implementations. ```typescript import { Tagify } from 'ngx-tagify'; const tagify: Tagify = new Tagify(inputElement); ``` ## Styling You have two options to include the styling of Tagify. __Option 1:__ Modify your `angular.json` by adding the `.scss` file to the `styles` property. ```json "options": { "styles": [ "node_modules/ngx-tagify/styles/tagify.scss", "src/styles.scss" ] } ``` __Option 2:__ If you want to override some of the styling, import it to a sass file. Have a look at [tagify/CSS Variables](https://github.com/yairEO/tagify#css-variables) and respective demo page for details. ```scss // src/styles.scss @use "ngx-tagify/styles/tagify"; .tagify --tags-border-color: #ff0000; ``` #### Usage with Bootstrap If you are using Bootstrap as CSS framework (as used in the demo), you might need to tweak some styles in order that Tagify looks pretty: ```scss .tagify { --tag-pad: 0.15rem 0.5rem; } .tagify__input { min-height: 1.25lh; } .tagify__input:empty::before { line-height: inherit; } .tagify.form-control { height: unset; } ``` ## FAQ ### I'm getting TS compilation error! You are getting TypeScript compilation error with an error output like this: ``` node_modules/@types/yaireo__tagify/index.d.ts:475:1 475 export = Tagify; ~~~~~~~~~~~~~~~~ This module is declared with using 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag. ``` To resolve this issue, set `allowSyntheticDefaultImports` within `compilerOptions` in your `tsconfig.json`: ```json { "compilerOptions": { "allowSyntheticDefaultImports": true, } } ``` ### I'm getting build error because stylesheet could not be found! ``` Module build failed (from ./node_modules/@angular-devkit/build-angular/node_modules/sass-loader/dist/cjs.js): Can't find stylesheet to import. ╷ 1 │ @import "@yaireo/tagify/src/tagify"; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ╵ node_modules/ngx-tagify/styles/tagify.scss 1:9 root stylesheet ``` Make sure you include `node_modules` in the `stylePreprocessorOptions` within your `angular.json`: ```json "stylePreprocessorOptions": { "includePaths": ["node_modules"] }, ```