Skip to content

Commit 91f2a69

Browse files
author
wangbeishan
committed
updated
1 parent 3836631 commit 91f2a69

10 files changed

+265
-14
lines changed

.npmignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Node generated files
2+
node_modules
3+
npm-debug.log
4+
.idea
5+
6+
# Ignored files
7+
# *.ts
8+
# *.js
9+
# *.map
10+
# *.d.ts

README.md

+71
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
RadioWithTextComponent,
4141
RadioGroupComponent,
4242
UploadImageComponent,
43+
UploadFileComponent,
4344
CheckboxGroupComponent,
4445
CheckboxWithTextComponent,
4546
DateTimePickerComponent,
@@ -55,6 +56,7 @@ import {
5556
RadioWithTextComponent,
5657
RadioGroupComponent,
5758
UploadImageComponent,
59+
UploadFileComponent,
5860
CheckboxGroupComponent,
5961
CheckboxWithTextComponent,
6062
DateTimePickerComponent,
@@ -93,6 +95,8 @@ export class YourModule {
9395
- 3 or 'year' for the 12-month overview (year view)
9496
- 4 or 'decade' for the 10-year overview. Useful for date-of-birth datetimepickers.
9597
- `minView`: `string`
98+
- `useTimestamp`: `boolean`
99+
- if `[(ngModel)]` use timestamp, default `false`
96100
- `disabled`: `boolean`
97101
- isDisabled: default false
98102
- `onSelect`
@@ -184,6 +188,73 @@ export class YourModule {
184188
- `disabled`: `boolean`
185189
- isDisabled: default false
186190

191+
6. select-with-input
192+
193+
**Template**
194+
195+
``` html
196+
<select-with-input [(ngModel)]='your_prop' [options]='your_options' [disabled]='your_condition' (onSelect)="onSelect($event)"></select-with-input>
197+
```
198+
199+
**Options**
200+
201+
- `options`: `option[]`
202+
- select options for select2
203+
- `option`: `{id: value, text: key}`
204+
- `ngModel`: option value that is selected or input value(`string`)
205+
- `onSelect`
206+
- callback when option selected
207+
- parmas: option value that is selected or input value(`string`)
208+
209+
7. upload-image
210+
211+
**Template**
212+
213+
``` html
214+
<upload-image [(ngModel)]='images_list_array' [limit]='limit_condition' [multiple]='if_multiple' [disabled]='your_condition' [btnName]='btn_name'></upload-image>
215+
```
216+
217+
**Options**
218+
219+
- `ngModel`: images array(`image base64 string[]`)
220+
- `limit`: limit conditions
221+
- `{width, height, size, type}`
222+
- `width`: image max width(px)
223+
- `height`: image max height(px)
224+
- `size`: image max size(k)
225+
- `type`: image type, accept 'jpeg'/'jpg'/'png'/'gif'
226+
- `multiple`: `boolean`
227+
- if upload multiple images, default true
228+
- `disabled`: `boolean`
229+
- isDisabled: default false
230+
- `btnName`: string
231+
- button display name
232+
233+
8. upload-file
234+
235+
**Template**
236+
237+
``` html
238+
<upload-file [(ngModel)]='your_prop' [multiple]='if_multiple' [limit]='limit_condition' [disabled]='your_condition' [btnName]="btn_name" ></upload-file>
239+
```
240+
241+
**Options**
242+
243+
- `ngModel`: files array(`file base64 string[]`)
244+
- `limit`: limit conditions
245+
- `{size, type}`
246+
- `size`: file max size(k)
247+
- `type`: file type, such as 'txt'
248+
- `multiple`: `boolean`
249+
- if upload multiple files, default true
250+
- `disabled`: `boolean`
251+
- isDisabled: default false
252+
- `btnName`: string
253+
- button display name
254+
- `dataType`: string
255+
- file reader data type
256+
- accept 'DataURL'/'ArrayBuffer'/'BinaryString'/'Text', default 'DataURL'
257+
187258
### Use Directive
188259

189260
1. ngFocusLost

index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export * from './src/radio-with-input.component';
66
export * from './src/select2.component';
77
export * from './src/select-with-input/select-with-input.component';
88
export * from './src/upload-image/upload-image.component';
9+
export * from './src/upload-file/upload-file.component';
910
export * from './src/on-focus-lost.directive';

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-form-components",
3-
"version": "0.0.7",
3+
"version": "0.1.0",
44
"description": "normal form component(select/select2/datetimepicker/checkbox-group/radio-group/...) in angular4(>=2.0-release)",
55
"main": "index.ts",
66
"scripts": {

src/date-time-picker.component.ts

+39-9
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,38 @@ require('./assets/datetimepicker/bootstrap-datetimepicker.js');
1212
import {Component, Input, AfterViewInit, ElementRef, EventEmitter, Output, ViewEncapsulation} from '@angular/core';
1313
import {CustomInputComponent, customInputAccessor} from './custom-input';
1414

15+
(function () {
16+
// Format Date into string.
17+
// month(M)、day(d)、hour(h)、minute(m)、second(s)、quarterly(q) , 1 or 2 character,
18+
// Samples:
19+
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
20+
// (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
21+
(Date as any).prototype.format = function (fmt) { //author: meizz
22+
var o = {
23+
"M+": this.getMonth() + 1, // Month
24+
"d+": this.getDate(), // Day
25+
"h+": this.getHours(), // Hour
26+
"m+": this.getMinutes(), // Minute
27+
"s+": this.getSeconds(), // Second
28+
"q+": Math.floor((this.getMonth() + 3) / 3), // Quarterly
29+
"S": this.getMilliseconds() // Millisecond
30+
};
31+
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
32+
for (var k in o)
33+
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
34+
return fmt;
35+
};
36+
})();
37+
38+
declare interface IDate extends Date{
39+
format: (fmt: any) => string;
40+
}
41+
1542
@Component({
1643
selector: 'date-time-picker',
17-
template: `<input type="text" class="form-control" [disabled]="disabled" [(ngModel)]="model" (blur)="onBlur()" />`,
44+
template: `<input type="text" class="form-control" [disabled]="disabled" [(ngModel)]="viewValue" (blur)="onBlur()" />`,
1845
styleUrls: ['./assets/datetimepicker/bootstrap-datetimepicker.css'],
19-
encapsulation: ViewEncapsulation.None,
46+
encapsulation: ViewEncapsulation.None,
2047
providers: [customInputAccessor(DateTimePickerComponent)]
2148
})
2249
// ControlValueAccessor: A bridge between a control and a native element.
@@ -75,11 +102,14 @@ export class DateTimePickerComponent implements AfterViewInit {
75102
format, // output format
76103
minView: parseInt(this.minView, 10) || minView || 0, // min view (default: hour view)
77104
})
78-
.on('hide', ev => {
79-
this.viewValue = $(ev.target).val(); // set value. (calling onChange())
80-
this.model = this.useTimestamp ? (new Date(this.model).getTime() / 1000) : this.viewValue;
81-
this.onSelect.emit({timestamp: this.model, text: this.viewValue}); // change event callback of input
82-
});
105+
.on('hide', ev => {
106+
this.viewValue = $(ev.target).val(); // set value. (calling onChange())
107+
// If use timestamp, change string into timestamp.
108+
//
109+
this.model = this.useTimestamp ? (Date.parse(this.viewValue) / 1000) : this.viewValue;
110+
this.onChange(this.model);
111+
this.onSelect.emit({timestamp: this.model, text: this.viewValue}); // change event callback of input
112+
});
83113
}
84114

85115
// Set touched on blur
@@ -89,10 +119,10 @@ export class DateTimePickerComponent implements AfterViewInit {
89119

90120
// Write a new value to the element.
91121
writeValue(value: string): void {
92-
if (value !== this.model) {
122+
if (value && value !== this.model) {
93123
this.model = value;
94124
const vd = new Date((+value) * 1000); // multiply 1000 for server timestamp
95-
this.viewValue = vd.format(this.formatStr);
125+
this.viewValue = (vd as IDate).format(this.formatStr);
96126
}
97127
}
98128

src/select-with-input/select-with-input.component.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* desc:select-with-input
3-
* how to use: <select-with-input [(ngModel)]='your_prop' [options]='your_options' [disabled]='your_condition' ></select-with-input>gi
3+
* how to use: <select-with-input [(ngModel)]='your_prop' [options]='your_options' [disabled]='your_condition' ></select-with-input>
44
*/
55

66
import { Component, Input, Output, EventEmitter } from '@angular/core';
@@ -21,7 +21,7 @@ interface IOption{
2121
export class SelectWithInputComponent {
2222
@Input() options: IOption[] = []; // object: {id, text}
2323
@Input() disabled: boolean = false;
24-
@Output() select = new EventEmitter<any>();
24+
@Output() onSelect = new EventEmitter<any>();
2525

2626
isOpen: boolean = false;
2727

@@ -34,6 +34,7 @@ export class SelectWithInputComponent {
3434
this.viewModel = option.text; // set view value
3535
this.model = option.id; // set inner value
3636
this.onChange(this.model);
37+
this.onSelect.emit(this.model);
3738
this.isOpen = false;
3839
}
3940

@@ -43,6 +44,7 @@ export class SelectWithInputComponent {
4344
const option = this.options && this.options.find(x => x.text === value);
4445
this.model = option ? option.id : value;
4546
this.onChange(this.model);
47+
this.onSelect.emit(this.model);
4648
}
4749

4850
isSelected(option: IOption){
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<span class="btn btn-info btn-file">
2+
<i class="fa fa-upload fa-fw"></i>
3+
{{btnName}}
4+
<input type="file" accept="*" [multiple]="multiple" (change)="upLoad()">
5+
</span>
6+
7+
<p *ngIf="help" class="help-block">{{help}}</p>
8+
9+
<div *ngFor="let f of filesArr">
10+
<p>{{f.name}}</p>
11+
</div>
12+
13+
<div *ngIf="checkErrArr.length" style="color: red;">
14+
<div *ngFor="let errArr of checkErrArr">
15+
<p><strong>{{errArr.name}}</strong></p>
16+
<p *ngFor="let err of errArr.checkErr">{{err}}</p>
17+
</div>
18+
</div>
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* desc:upload-file
3+
* how to use: <upload-file [(ngModel)]='your_prop' [multiple]='if_multiple' [limit]='limit_condition' [disabled]='your_condition' [btnName]="btn_name" ></upload-file>
4+
*/
5+
import {Component, Input, OnInit, ElementRef} from '@angular/core';
6+
import {customInputAccessor} from '../custom-input';
7+
8+
export interface IFileLimit {
9+
size?: number;
10+
type?: string;
11+
}
12+
13+
@Component({
14+
selector: 'upload-file',
15+
templateUrl: './upload-file.component.html',
16+
providers: [customInputAccessor(UploadFileComponent)]
17+
})
18+
export class UploadFileComponent implements OnInit {
19+
@Input() disabled: boolean = false;
20+
@Input() required: boolean = false;
21+
@Input() limit: IFileLimit = {};
22+
@Input() btnName: string = 'upload files';
23+
@Input() multiple: boolean = true;
24+
@Input() dataType: string = 'DataURL'; // Can be 'DataURL'/'ArrayBuffer'/'BinaryString'/'Text'
25+
26+
filesArr: any = [];
27+
help: string = '';
28+
checkErrArr: any = [];
29+
30+
private model: string[] = []; // urls array chosen
31+
private onChange: (_: any) => void;
32+
private onTouched: () => void;
33+
34+
constructor(private el: ElementRef) {
35+
}
36+
37+
ngOnInit() {
38+
// Set help text.
39+
if (this.required) {
40+
this.help += 'required. ';
41+
}
42+
if (this.limit) {
43+
if (this.limit.size) {
44+
this.help = this.help ? this.help + ', <= ' + this.limit.size + 'k' : '<= ' + this.limit.size + 'k';
45+
}
46+
if (this.limit.type) {
47+
this.help = this.help ? this.help + ', type: ' + this.limit.type : 'Type: ' + this.limit.type;
48+
}
49+
}
50+
}
51+
52+
// when file input change
53+
upLoad() {
54+
const me = this;
55+
this.filesArr = [];
56+
this.model = [];
57+
this.checkErrArr = [];
58+
// Get files.
59+
const input = $(this.el.nativeElement).find('input')[0];
60+
const files = input.files;
61+
if (files) {
62+
// Travelsal files.
63+
Object.keys(files).forEach(index => {
64+
const file = files[index];
65+
const regMap = new RegExp(`\.${this.limit.type}$`, 'i');
66+
const reader: FileReader = new FileReader();
67+
68+
reader.onload = (e: ProgressEvent) => {
69+
const url = reader.result;
70+
const name = file.name;
71+
const checkErr = [];
72+
// If image is not valid, write down the file name and error message.
73+
if (this.limit.size && file.size > this.limit.size * 1024) {
74+
checkErr.push('File should no more than ' + this.limit.size + ' K. ');
75+
}
76+
if (this.limit.type && !regMap.test(file.name)) {
77+
checkErr.push('File type should be ' + this.limit.type);
78+
}
79+
if (!checkErr.length) {
80+
// If image is valid, push into model array
81+
this.filesArr.push({name, url});
82+
this.model.push(url);
83+
this.onChange(this.model);
84+
} else {
85+
this.checkErrArr.push({name, checkErr});
86+
}
87+
};
88+
// reader
89+
reader[`readAs${me.dataType}`](file);
90+
});
91+
}
92+
}
93+
94+
// Set touched on blur
95+
onBlur() {
96+
this.onTouched();
97+
}
98+
99+
// Write a new value to the element.
100+
writeValue(value: string[]): void {
101+
if (value && value.length) {
102+
this.model = value;
103+
this.filesArr = this.model.map(url => {
104+
return {url, name: ''};
105+
});
106+
}
107+
}
108+
109+
// Set the function to be called when the control receives a change event.
110+
registerOnChange(fn: (_: any) => {}): void {
111+
this.onChange = fn;
112+
}
113+
114+
// registerOnTouched(fn: any) : void
115+
registerOnTouched(fn: () => {}): void {
116+
this.onTouched = fn;
117+
}
118+
}

src/upload-image/upload-image.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<span class="btn btn-info btn-file">
22
<i class="fa fa-upload fa-fw"></i>
33
{{btnName}}
4-
<input type="file" accept="image/*" multiple="multiple" (change)="upLoad()">
4+
<input type="file" accept="image/*" [multiple]="multiple" (change)="upLoad()">
55
</span>
66

77
<p *ngIf="help" class="help-block">{{help}}</p>

src/upload-image/upload-image.component.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* desc:upload-image
3-
* how to use: <upload-image [(ngModel)]='your_prop' [limit]='limit_condition' [disabled]='your_condition' [btnName]="btn_name" ></upload-image>
3+
* how to use: <upload-image [(ngModel)]='your_prop' [multiple]='if_multiple' [limit]='limit_condition' [disabled]='your_condition' [btnName]="btn_name" ></upload-image>
44
*/
55
import {Component, Input, OnInit, ElementRef} from '@angular/core';
66
import {customInputAccessor} from '../custom-input';
@@ -22,6 +22,7 @@ export class UploadImageComponent implements OnInit {
2222
@Input() required: boolean = false;
2323
@Input() limit: ILimit = {};
2424
@Input() btnName: string = 'upload images';
25+
@Input() multiple: boolean = true;
2526

2627
imagesArr: any = [];
2728
help: string = '';

0 commit comments

Comments
 (0)