@libs-ui/components-inputs-add 0.2.355-8 → 0.2.356-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +319 -2
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,3 +1,320 @@
|
|
|
1
|
-
# inputs-add
|
|
1
|
+
# @libs-ui/components-inputs-add
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Component cho phép thêm/xóa nhiều input fields động với validation và customization đầy đủ
|
|
4
|
+
|
|
5
|
+
**Version:** 0.2.355-14
|
|
6
|
+
|
|
7
|
+
## Giới thiệu
|
|
8
|
+
|
|
9
|
+
`LibsUiComponentsInputsAddComponent` là một standalone Angular component để quản lý danh sách các input fields động. Component cho phép người dùng thêm/xóa fields, validate từng field riêng biệt, và kiểm tra duplicate values.
|
|
10
|
+
|
|
11
|
+
### Tính năng
|
|
12
|
+
|
|
13
|
+
- ✅ Thêm/xóa input fields động
|
|
14
|
+
- ✅ Validation cho từng field riêng biệt
|
|
15
|
+
- ✅ Kiểm tra duplicate values
|
|
16
|
+
- ✅ Min/Max items configuration
|
|
17
|
+
- ✅ Custom button configuration
|
|
18
|
+
- ✅ OnPush Change Detection
|
|
19
|
+
- ✅ Angular Signals
|
|
20
|
+
- ✅ Two-way binding với items array
|
|
21
|
+
|
|
22
|
+
## Khi nào sử dụng
|
|
23
|
+
|
|
24
|
+
- Cần nhập nhiều giá trị cùng loại (emails, phone numbers, URLs...)
|
|
25
|
+
- Cho phép người dùng thêm/xóa fields động
|
|
26
|
+
- Cần validation cho từng field riêng biệt
|
|
27
|
+
- Quản lý danh sách inputs với min/max items
|
|
28
|
+
- Cần kiểm tra duplicate values giữa các fields
|
|
29
|
+
|
|
30
|
+
## Important Notes
|
|
31
|
+
|
|
32
|
+
⚠️ **fieldNameBind Required**: Phải truyền fieldNameBind để xác định property name trong items array.
|
|
33
|
+
|
|
34
|
+
⚠️ **items Model**: items là model (two-way binding), component tự động cập nhật array khi add/remove.
|
|
35
|
+
|
|
36
|
+
⚠️ **Validation Timing**: Validation chạy sau 250ms debounce khi value change hoặc remove item.
|
|
37
|
+
|
|
38
|
+
⚠️ **ignoreValidEmptyField**: Khi true, chỉ cần ít nhất 1 field có giá trị là valid (không bắt buộc tất cả).
|
|
39
|
+
|
|
40
|
+
## Cài đặt
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# npm
|
|
44
|
+
npm install @libs-ui/components-inputs-add
|
|
45
|
+
|
|
46
|
+
# yarn
|
|
47
|
+
yarn add @libs-ui/components-inputs-add
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Import
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { LibsUiComponentsInputsAddComponent, IInputAdd, IInputAddFunctionControlEvent } from '@libs-ui/components-inputs-add';
|
|
54
|
+
|
|
55
|
+
@Component({
|
|
56
|
+
standalone: true,
|
|
57
|
+
imports: [LibsUiComponentsInputsAddComponent],
|
|
58
|
+
// ...
|
|
59
|
+
})
|
|
60
|
+
export class YourComponent {}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Ví dụ
|
|
64
|
+
|
|
65
|
+
### Basic
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<libs_ui-components-inputs-add
|
|
69
|
+
[fieldNameBind]="'email'"
|
|
70
|
+
[(items)]="emailItems"
|
|
71
|
+
[placeholder]="'Nhập email'"
|
|
72
|
+
[labelConfig]="{
|
|
73
|
+
labelLeft: 'Email addresses',
|
|
74
|
+
required: true
|
|
75
|
+
}" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export class ExampleComponent {
|
|
80
|
+
emailItems = signal<Array<IInputAdd>>([{ email: '' }]);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Min/Max Items
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<libs_ui-components-inputs-add
|
|
88
|
+
[fieldNameBind]="'phone'"
|
|
89
|
+
[(items)]="phoneItems"
|
|
90
|
+
[minItems]="2"
|
|
91
|
+
[maxItems]="10"
|
|
92
|
+
[placeholder]="'Nhập số điện thoại'" />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### With Validation & Duplicate Check
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<libs_ui-components-inputs-add
|
|
99
|
+
[fieldNameBind]="'url'"
|
|
100
|
+
[(items)]="urlItems"
|
|
101
|
+
[placeholder]="'https://example.com'"
|
|
102
|
+
[validRequired]="{
|
|
103
|
+
isRequired: true,
|
|
104
|
+
message: 'URL is required'
|
|
105
|
+
}"
|
|
106
|
+
[validDuplicate]="{
|
|
107
|
+
message: 'URL already exists'
|
|
108
|
+
}"
|
|
109
|
+
(outValueChange)="onValueChange($event)"
|
|
110
|
+
(outFunctionsControl)="onFunctionsControl($event)" />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export class ExampleComponent {
|
|
115
|
+
urlItems = signal<Array<IInputAdd>>([{ url: '' }]);
|
|
116
|
+
functionControls = signal<IInputAddFunctionControlEvent | null>(null);
|
|
117
|
+
|
|
118
|
+
onValueChange(event: IEmitValueChange) {
|
|
119
|
+
console.log('Value changed:', event.value, event.item);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async validateAll() {
|
|
123
|
+
const isValid = await this.functionControls()?.checkIsValid();
|
|
124
|
+
console.log('Is valid:', isValid);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Custom Button & Ignore Empty Field Validation
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<libs_ui-components-inputs-add
|
|
133
|
+
[fieldNameBind]="'tag'"
|
|
134
|
+
[(items)]="tagItems"
|
|
135
|
+
[maxItems]="8"
|
|
136
|
+
[ignoreValidEmptyField]="true"
|
|
137
|
+
[addItemButtonConfig]="{
|
|
138
|
+
type: 'button-primary',
|
|
139
|
+
label: 'Add Tag',
|
|
140
|
+
classIconLeft: 'libs-ui-icon-add'
|
|
141
|
+
}"
|
|
142
|
+
[validRequired]="{
|
|
143
|
+
isRequired: true,
|
|
144
|
+
message: 'At least one tag is required'
|
|
145
|
+
}" />
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## API
|
|
149
|
+
|
|
150
|
+
### libs_ui-components-inputs-add
|
|
151
|
+
|
|
152
|
+
#### Inputs
|
|
153
|
+
|
|
154
|
+
| Property | Type | Default | Description |
|
|
155
|
+
| ---------------------------------- | -------------------------------- | ----------------------------------------- | ----------------------------------------- |
|
|
156
|
+
| `[acceptNegativeValue]` | `boolean` | `false` | Cho phép giá trị âm (với dataType number) |
|
|
157
|
+
| `[addItemButtonConfig]` | `IButton` | `undefined` | Cấu hình button "Add New" |
|
|
158
|
+
| `[autoAddZeroLessThan10InTypeInt]` | `boolean` | `false` | Tự động thêm số 0 nếu < 10 (kiểu int) |
|
|
159
|
+
| `[autoRemoveEmoji]` | `boolean` | `false` | Tự động loại bỏ emoji khỏi input |
|
|
160
|
+
| `[backgroundNone]` | `boolean` | `false` | Sử dụng background trong suốt |
|
|
161
|
+
| `[borderError]` | `boolean` | `false` | Hiển thị border màu đỏ khi có lỗi |
|
|
162
|
+
| `[classContainerBottomInput]` | `string` | `undefined` | Class CSS cho container dưới input |
|
|
163
|
+
| `[classContainerInput]` | `string` | `undefined` | Class CSS cho container bao quanh input |
|
|
164
|
+
| `[classInclude]` | `string` | `undefined` | Class CSS cho container chính |
|
|
165
|
+
| `[classIncludeInput]` | `string` | `undefined` | Class CSS cho mỗi input field |
|
|
166
|
+
| `[classMessageErrorInclude]` | `string` | `undefined` | Class CSS cho message lỗi |
|
|
167
|
+
| `[configItemAddToItems]` | `IInputAdd` | `undefined` | Template config khi add item mới |
|
|
168
|
+
| `[configUnitLeft]` | `IInputValidUnitConfig` | `{ fieldKey: "id", fieldLabel: "label" }` | Cấu hình hiển thị unit bên trái |
|
|
169
|
+
| `[configUnitRight]` | `IInputValidUnitConfig` | `{ fieldKey: "id", fieldLabel: "label" }` | Cấu hình hiển thị unit bên phải |
|
|
170
|
+
| `[dataType]` | `TYPE_DATA_TYPE_INPUT` | `undefined` | Loại dữ liệu: text, number, email... |
|
|
171
|
+
| `[defaultHeight]` | `number` | `undefined` | Chiều cao mặc định |
|
|
172
|
+
| `[disable]` | `boolean` | `false` | Disable tất cả inputs |
|
|
173
|
+
| `[emitEmptyInDataTypeNumber]` | `boolean` | `false` | Emit giá trị rỗng khi dataType là number |
|
|
174
|
+
| `[fieldNameBind]` | `string` | `required` | Tên property trong items array |
|
|
175
|
+
| `[fixedFloat]` | `number` | `undefined` | Số lượng số lẻ sau dấu phẩy |
|
|
176
|
+
| `[focusTimeOut]` | `number` | `undefined` | Thời gian delay focus |
|
|
177
|
+
| `[formInputSpacing]` | `number` | `8` | Khoảng cách giữa các input (px) |
|
|
178
|
+
| `[functionValid]` | `TYPE_FUNCTION_INPUT_VALID` | `undefined` | Hàm validate tùy chỉnh |
|
|
179
|
+
| `[ignoreAddItem]` | `boolean` | `false` | Ẩn button "Add New" |
|
|
180
|
+
| `[ignoreRemove]` | `boolean` | `false` | Ẩn button remove |
|
|
181
|
+
| `[ignoreShowError]` | `boolean` | `false` | Ẩn hiển thị lỗi |
|
|
182
|
+
| `[ignoreStopPropagationEvent]` | `boolean` | `false` | Không chặn sự kiện lan truyền |
|
|
183
|
+
| `[ignoreUnitRightClassReadOnly]` | `boolean` | `false` | Bỏ class readonly cho unit phải |
|
|
184
|
+
| `[ignoreValidEmptyField]` | `boolean` | `false` | Chỉ cần ít nhất 1 field có giá trị |
|
|
185
|
+
| `[ignoreWidthInput100]` | `boolean` | `false` | Bỏ width 100% cho input |
|
|
186
|
+
| `[isBaselineStyle]` | `boolean` | `false` | Căn chỉnh baseline items |
|
|
187
|
+
| `[items]` | `Array<IInputAdd>` | `required` | Array items (model binding) |
|
|
188
|
+
| `[keepPlaceholderOnly]` | `boolean` | `false` | Chỉ giữ placeholder |
|
|
189
|
+
| `[keySelectedUnitLeft]` | `any` | `undefined` | Key unit trái đang chọn |
|
|
190
|
+
| `[keySelectedUnitRight]` | `any` | `undefined` | Key unit phải đang chọn |
|
|
191
|
+
| `[labelConfig]` | `ILabel` | `undefined` | Cấu hình label cho component |
|
|
192
|
+
| `[leftTemplateItems]` | `ITemplateRightLeftItem` | `undefined` | Template item bên trái |
|
|
193
|
+
| `[maxItems]` | `number` | `5` | Số lượng items tối đa |
|
|
194
|
+
| `[maxLength]` | `number` | `undefined` | Độ dài tối đa cho input |
|
|
195
|
+
| `[maxLengthNumberCount]` | `number` | `undefined` | Max length cho number count |
|
|
196
|
+
| `[maxValueNumber]` | `number` | `undefined` | Giá trị số tối đa |
|
|
197
|
+
| `[minItems]` | `number` | `1` | Số lượng items tối thiểu |
|
|
198
|
+
| `[minValueNumber]` | `number` | `undefined` | Giá trị số tối thiểu |
|
|
199
|
+
| `[noBorder]` | `boolean` | `false` | Không hiển thị border |
|
|
200
|
+
| `[onlyAcceptNegativeValue]` | `boolean` | `false` | Chỉ chấp nhận số âm |
|
|
201
|
+
| `[paddingRightCustomSpecific]` | `number` | `undefined` | Custom padding phải |
|
|
202
|
+
| `[placeholder]` | `string` | `undefined` | Placeholder cho inputs |
|
|
203
|
+
| `[positionMessageErrorStartInput]` | `boolean` | `false` | Hiển thị lỗi bắt đầu từ input |
|
|
204
|
+
| `[readonly]` | `boolean` | `false` | Readonly tất cả inputs |
|
|
205
|
+
| `[resetAutoCompletePassword]` | `boolean` | `false` | Reset autocomplete password |
|
|
206
|
+
| `[resize]` | `'auto' \| 'none'` | `undefined` | Khả năng resize |
|
|
207
|
+
| `[rightTemplateItems]` | `ITemplateRightLeftItem` | `undefined` | Template item bên phải |
|
|
208
|
+
| `[showCount]` | `boolean` | `false` | Hiển thị đếm ký tự |
|
|
209
|
+
| `[tagInput]` | `TYPE_TAG_INPUT` | `undefined` | Tag input (textarea, input...) |
|
|
210
|
+
| `[templateLeftBottomInput]` | `TemplateRef<TYPE_TEMPLATE_REF>` | `undefined` | Template dưới trái input |
|
|
211
|
+
| `[templateLeftOutlet]` | `TemplateRef<TYPE_TEMPLATE_REF>` | `undefined` | Template outlet bên trái |
|
|
212
|
+
| `[templateRightBottomInput]` | `TemplateRef<TYPE_TEMPLATE_REF>` | `undefined` | Template dưới phải input |
|
|
213
|
+
| `[templateRightOutlet]` | `TemplateRef<TYPE_TEMPLATE_REF>` | `undefined` | Template outlet bên phải |
|
|
214
|
+
| `[typeInput]` | `TYPE_INPUT` | `undefined` | Loại input (text, password...) |
|
|
215
|
+
| `[unitsLeft]` | `Array<any>` | `undefined` | Danh sách units bên trái |
|
|
216
|
+
| `[unitsRight]` | `Array<any>` | `undefined` | Danh sách units bên phải |
|
|
217
|
+
| `[useColorModeExist]` | `boolean` | `false` | Sử dụng color mode có sẵn |
|
|
218
|
+
| `[validDuplicate]` | `IMessageTranslate` | `undefined` | Message khi có giá trị duplicate |
|
|
219
|
+
| `[validMaxValue]` | `IMessageTranslate` | `undefined` | Validate giá trị tối đa |
|
|
220
|
+
| `[validMaxLength]` | `IMessageTranslate` | `undefined` | Validate độ dài tối đa |
|
|
221
|
+
| `[validMinLength]` | `IValidLength` | `undefined` | Validate độ dài tối thiểu |
|
|
222
|
+
| `[validMinValue]` | `IMessageTranslate` | `undefined` | Validate giá trị tối thiểu |
|
|
223
|
+
| `[validPattern]` | `Array<IValidPattern>` | `undefined` | Validate theo pattern regex |
|
|
224
|
+
| `[validRequired]` | `IValidRequired` | `undefined` | Validation required |
|
|
225
|
+
| `[valuePatternShowError]` | `boolean` | `false` | Hiển thị lỗi pattern |
|
|
226
|
+
| `[valueUpDownNumber]` | `number` | `undefined` | Bước nhảy tăng giảm số |
|
|
227
|
+
|
|
228
|
+
#### Outputs
|
|
229
|
+
|
|
230
|
+
| Property | Type | Description |
|
|
231
|
+
| ----------------------- | ------------------------------- | -------------------------------- |
|
|
232
|
+
| `(outAddItem)` | `IInputAdd` | Emit khi thêm item mới |
|
|
233
|
+
| `(outClickButtonLabel)` | `IButton` | Emit khi click button trên label |
|
|
234
|
+
| `(outFunctionsControl)` | `IInputAddFunctionControlEvent` | Emit function controls |
|
|
235
|
+
| `(outLabelLeftClick)` | `MouseEvent` | Emit khi click label trái |
|
|
236
|
+
| `(outLabelRightClick)` | `boolean` | Emit khi click label phải |
|
|
237
|
+
| `(outRemove)` | `IInputAdd` | Emit khi xóa item |
|
|
238
|
+
| `(outSwitchEventLabel)` | `ISwitchEvent` | Emit sự kiện switch trên label |
|
|
239
|
+
| `(outValueChange)` | `IEmitValueChange` | Emit khi giá trị thay đổi |
|
|
240
|
+
|
|
241
|
+
#### FunctionsControl Methods
|
|
242
|
+
|
|
243
|
+
| Method | Description |
|
|
244
|
+
| -------------------------- | --------------------------------------------------------- |
|
|
245
|
+
| `checkIsValid()` | Kiểm tra validation tất cả items, trả về Promise<boolean> |
|
|
246
|
+
| `setMessageError(message)` | Set error message cho tất cả items |
|
|
247
|
+
|
|
248
|
+
## Types & Interfaces
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
interface IInputAdd extends Record<string, any> {
|
|
252
|
+
uniqKey?: string;
|
|
253
|
+
disable?: boolean;
|
|
254
|
+
readonly?: boolean;
|
|
255
|
+
ignoreRemove?: boolean;
|
|
256
|
+
labelConfig?: ILabel;
|
|
257
|
+
placeholder?: string;
|
|
258
|
+
maxValueNumber?: number;
|
|
259
|
+
minValueNumber?: number;
|
|
260
|
+
maxLength?: number;
|
|
261
|
+
|
|
262
|
+
unitsLeft?: Array<any>;
|
|
263
|
+
configUnitLeft?: IInputValidUnitConfig;
|
|
264
|
+
keySelectedUnitLeft?: any;
|
|
265
|
+
|
|
266
|
+
unitsRight?: Array<any>;
|
|
267
|
+
configUnitRight?: IInputValidUnitConfig;
|
|
268
|
+
keySelectedUnitRight?: any;
|
|
269
|
+
|
|
270
|
+
templateLeftOutlet?: TemplateRef<TYPE_TEMPLATE_REF>;
|
|
271
|
+
templateRightOutlet?: TemplateRef<TYPE_TEMPLATE_REF>;
|
|
272
|
+
functionControl?: IInputValidFunctionControlEvent;
|
|
273
|
+
|
|
274
|
+
[key: string]: any;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
interface IInputAddFunctionControlEvent {
|
|
278
|
+
checkIsValid: () => Promise<boolean>;
|
|
279
|
+
setMessageError: (message: string) => Promise<void>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface IEmitValueChange {
|
|
283
|
+
value: string | number;
|
|
284
|
+
item: IInputAdd;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Công nghệ
|
|
289
|
+
|
|
290
|
+
| Technology | Version | Purpose |
|
|
291
|
+
| --------------- | ------- | ---------------- |
|
|
292
|
+
| Angular | 18+ | Framework |
|
|
293
|
+
| Angular Signals | - | State management |
|
|
294
|
+
| TailwindCSS | 3.x | Styling |
|
|
295
|
+
| OnPush | - | Change Detection |
|
|
296
|
+
|
|
297
|
+
## Demo
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
npx nx serve core-ui
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Truy cập: `http://localhost:4500/inputs/add`
|
|
304
|
+
|
|
305
|
+
## Unit Tests
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Chạy tests
|
|
309
|
+
npx nx test components-inputs-add
|
|
310
|
+
|
|
311
|
+
# Coverage
|
|
312
|
+
npx nx test components-inputs-add --coverage
|
|
313
|
+
|
|
314
|
+
# Watch mode
|
|
315
|
+
npx nx test components-inputs-add --watch
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/components-inputs-add",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.356-0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": ">=18.0.0",
|
|
6
6
|
"@angular/core": ">=18.0.0",
|
|
7
|
-
"@libs-ui/components-buttons-button": "0.2.
|
|
8
|
-
"@libs-ui/components-inputs-valid": "0.2.
|
|
9
|
-
"@libs-ui/components-label": "0.2.
|
|
10
|
-
"@libs-ui/components-switch": "0.2.
|
|
11
|
-
"@libs-ui/interfaces-types": "0.2.
|
|
12
|
-
"@libs-ui/utils": "0.2.
|
|
7
|
+
"@libs-ui/components-buttons-button": "0.2.356-0",
|
|
8
|
+
"@libs-ui/components-inputs-valid": "0.2.356-0",
|
|
9
|
+
"@libs-ui/components-label": "0.2.356-0",
|
|
10
|
+
"@libs-ui/components-switch": "0.2.356-0",
|
|
11
|
+
"@libs-ui/interfaces-types": "0.2.356-0",
|
|
12
|
+
"@libs-ui/utils": "0.2.356-0",
|
|
13
13
|
"@ngx-translate/core": "^15.0.0",
|
|
14
|
-
"@libs-ui/components-inputs-input": "0.2.
|
|
14
|
+
"@libs-ui/components-inputs-input": "0.2.356-0"
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"module": "fesm2022/libs-ui-components-inputs-add.mjs",
|