<div *ngIf="information" class="alert alert-success">{{ information }}</div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-<form role="form" (ngSubmit)="changePassword()" [formGroup]="changePasswordForm">
+<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
<div class="form-group">
<label for="new-password">New password</label>
<input
- type="password" class="form-control" name="new-password" id="new-password"
- [(ngModel)]="newPassword" #newPasswordInput="ngModel"
+ type="password" class="form-control" id="new-password"
+ formControlName="new-password"
>
- <div [hidden]="changePasswordForm.controls['new-password'].valid || changePasswordForm.controls['new-password'].pristine" class="alert alert-warning">
- The password should have more than 5 characters
+ <div *ngIf="formErrors['new-password']" class="alert alert-danger">
+ {{ formErrors['new-password'] }}
</div>
</div>
<div class="form-group">
<label for="name">Confirm new password</label>
<input
- type="password" class="form-control" name="new-confirmed-password" id="new-confirmed-password"
- [(ngModel)]="newConfirmedPassword" #newConfirmedPasswordInput="ngModel"
+ type="password" class="form-control" id="new-confirmed-password"
+ formControlName="new-confirmed-password"
>
</div>
- <input type="submit" value="Change password" class="btn btn-default" [disabled]="!changePasswordForm.valid">
+ <input type="submit" value="Change password" class="btn btn-default" [disabled]="!form.valid">
</form>
import { } from '@angular/common';
import { Component, OnInit } from '@angular/core';
-import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { AccountService } from './account.service';
+import { FormReactive, USER_PASSWORD } from '../shared';
@Component({
selector: 'my-account',
template: require('./account.component.html')
})
-export class AccountComponent implements OnInit {
- newPassword = '';
- newConfirmedPassword = '';
- changePasswordForm: FormGroup;
+export class AccountComponent extends FormReactive implements OnInit {
information: string = null;
error: string = null;
+ form: FormGroup;
+ formErrors = {
+ 'new-password': '',
+ 'new-confirmed-password': ''
+ };
+ validationMessages = {
+ 'new-password': USER_PASSWORD.MESSAGES,
+ 'new-confirmed-password': USER_PASSWORD.MESSAGES
+ };
+
constructor(
private accountService: AccountService,
+ private formBuilder: FormBuilder,
private router: Router
- ) {}
+ ) {
+ super();
+ }
- ngOnInit() {
- this.changePasswordForm = new FormGroup({
- 'new-password': new FormControl('', [ <any>Validators.required, <any>Validators.minLength(6) ]),
- 'new-confirmed-password': new FormControl('', [ <any>Validators.required, <any>Validators.minLength(6) ]),
+ buildForm() {
+ this.form = this.formBuilder.group({
+ 'new-password': [ '', USER_PASSWORD.VALIDATORS ],
+ 'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ],
});
+
+ this.form.valueChanges.subscribe(data => this.onValueChanged(data));
+ }
+
+ ngOnInit() {
+ this.buildForm();
}
changePassword() {
+ const newPassword = this.form.value['new-password'];
+ const newConfirmedPassword = this.form.value['new-confirmed-password'];
+
this.information = null;
this.error = null;
- if (this.newPassword !== this.newConfirmedPassword) {
+ if (newPassword !== newConfirmedPassword) {
this.error = 'The new password and the confirmed password do not correspond.';
return;
}
- this.accountService.changePassword(this.newPassword).subscribe(
+ this.accountService.changePassword(newPassword).subscribe(
ok => this.information = 'Password updated.',
err => this.error = err
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-<form (ngSubmit)="makeFriends()" [formGroup]="friendAddForm">
+<form (ngSubmit)="makeFriends()" [formGroup]="form">
<div class="form-group" *ngFor="let url of urls; let id = index; trackBy:customTrackBy">
<label for="username">Url</label>
<div class="input-group">
<input
type="text" class="form-control" placeholder="http://domain.com"
- [name]="'url-' + id" [id]="'url-' + id" [formControlName]="'url-' + id" [(ngModel)]="urls[id]"
+ [id]="'url-' + id" [formControlName]="'url-' + id"
/>
<span class="input-group-btn">
<button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button>
</span>
</div>
- <div [hidden]="friendAddForm.controls['url-' + id].valid || friendAddForm.controls['url-' + id].pristine" class="alert alert-warning">
+ <div [hidden]="form.controls['url-' + id].valid || form.controls['url-' + id].pristine" class="alert alert-warning">
It should be a valid url.
</div>
</div>
styles: [ require('./friend-add.component.scss') ]
})
export class FriendAddComponent implements OnInit {
- friendAddForm: FormGroup;
+ form: FormGroup;
urls = [ ];
error: string = null;
constructor(private router: Router, private friendService: FriendService) {}
ngOnInit() {
- this.friendAddForm = new FormGroup({});
+ this.form = new FormGroup({});
this.addField();
}
addField() {
- this.friendAddForm.addControl(`url-${this.urls.length}`, new FormControl('', [ validateUrl ]));
+ this.form.addControl(`url-${this.urls.length}`, new FormControl('', [ validateUrl ]));
this.urls.push('');
}
isFormValid() {
// Do not check the last input
for (let i = 0; i < this.urls.length - 1; i++) {
- if (!this.friendAddForm.controls[`url-${i}`].valid) return false;
+ if (!this.form.controls[`url-${i}`].valid) return false;
}
const lastIndex = this.urls.length - 1;
if (this.urls[lastIndex] === '' && lastIndex !== 0) {
return true;
} else {
- return this.friendAddForm.controls[`url-${lastIndex}`].valid;
+ return this.form.controls[`url-${lastIndex}`].valid;
}
}
removeField(index: number) {
// Remove the last control
- this.friendAddForm.removeControl(`url-${this.urls.length - 1}`);
+ this.form.removeControl(`url-${this.urls.length - 1}`);
this.urls.splice(index, 1);
}
private getNotEmptyUrls() {
const notEmptyUrls = [];
- this.urls.forEach((url) => {
+ Object.keys(this.form.value).forEach((urlKey) => {
+ const url = this.form.value[urlKey];
if (url !== '') notEmptyUrls.push(url);
});
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-<form role="form" (ngSubmit)="addUser()" [formGroup]="userAddForm">
+<form role="form" (ngSubmit)="addUser()" [formGroup]="form">
<div class="form-group">
<label for="username">Username</label>
<input
- type="text" class="form-control" name="username" id="username" placeholder="Username"
- [(ngModel)]="username"
+ type="text" class="form-control" id="username" placeholder="Username"
+ formControlName="username"
>
- <div [hidden]="userAddForm.controls.username.valid || userAddForm.controls.username.pristine" class="alert alert-danger">
- Username is required with a length >= 3 and <= 20
+ <div *ngIf="formErrors.username" class="alert alert-danger">
+ {{ formErrors.username }}
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
- type="password" class="form-control" name="password" id="password" placeholder="Password"
- [(ngModel)]="password"
+ type="password" class="form-control" id="password" placeholder="Password"
+ formControlName="password"
>
- <div [hidden]="userAddForm.controls.password.valid || userAddForm.controls.password.pristine" class="alert alert-danger">
- Password is required with a length >= 6
+ <div *ngIf="formErrors.password" class="alert alert-danger">
+ {{ formErrors.password }}
</div>
</div>
- <input type="submit" value="Add user" class="btn btn-default" [disabled]="!userAddForm.valid">
+ <input type="submit" value="Add user" class="btn btn-default" [disabled]="!form.valid">
</form>
import { Component, OnInit } from '@angular/core';
-import { FormGroup, FormControl, Validators } from '@angular/forms';
+import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { UserService } from '../shared';
+import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared';
@Component({
selector: 'my-user-add',
template: require('./user-add.component.html')
})
-export class UserAddComponent implements OnInit {
- userAddForm: FormGroup;
+export class UserAddComponent extends FormReactive implements OnInit {
error: string = null;
- username = '';
- password = '';
- constructor(private router: Router, private userService: UserService) {}
+ form: FormGroup;
+ formErrors = {
+ 'username': '',
+ 'password': ''
+ };
+ validationMessages = {
+ 'username': USER_USERNAME.MESSAGES,
+ 'password': USER_PASSWORD.MESSAGES,
+ };
- ngOnInit() {
- this.userAddForm = new FormGroup({
- username: new FormControl('', [ <any>Validators.required, <any>Validators.minLength(3), <any>Validators.maxLength(20) ]),
- password: new FormControl('', [ <any>Validators.required, <any>Validators.minLength(6) ]),
+ constructor(
+ private formBuilder: FormBuilder,
+ private router: Router,
+ private userService: UserService
+ ) {
+ super();
+ }
+
+ buildForm() {
+ this.form = this.formBuilder.group({
+ username: [ '', USER_USERNAME.VALIDATORS ],
+ password: [ '', USER_PASSWORD.VALIDATORS ],
});
+
+ this.form.valueChanges.subscribe(data => this.onValueChanged(data));
+ }
+
+ ngOnInit() {
+ this.buildForm();
}
addUser() {
this.error = null;
- this.userService.addUser(this.username, this.password).subscribe(
+ const { username, password } = this.form.value;
+
+ this.userService.addUser(username, password).subscribe(
ok => this.router.navigate([ '/admin/users/list' ]),
err => this.error = err.text
VideoMiniatureComponent,
VideoSortComponent,
VideoWatchComponent,
- VideoService
+ VideoService,
+ WebTorrentService
} from './videos';
import {
FriendsComponent,
AuthService,
RestExtractor,
- RestExtractor, RestService, VideoService, SearchService, FriendService, UserService, AccountService
+ RestExtractor, RestService, VideoService, SearchService, FriendService, UserService, AccountService, WebTorrentService
];
/**
* `AppModule` is the main entry point into Angular2's bootstraping process
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-<form role="form" (ngSubmit)="login()" [formGroup]="loginForm">
+<form role="form" (ngSubmit)="login()" [formGroup]="form">
<div class="form-group">
<label for="username">Username</label>
<input
- type="text" class="form-control" name="username" id="username" placeholder="Username"
- [(ngModel)]="username"
+ type="text" class="form-control" id="username" placeholder="Username" required
+ formControlName="username"
>
- <div [hidden]="loginForm.controls.username.valid || loginForm.controls.username.pristine" class="alert alert-danger">
- Username is required
+ <div *ngIf="formErrors.username" class="alert alert-danger">
+ {{ formErrors.username }}
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
- type="password" class="form-control" name="password" id="password" placeholder="Password"
- [(ngModel)]="password"
+ type="password" class="form-control" name="password" id="password" placeholder="Password" required
+ formControlName="password"
>
- <div [hidden]="loginForm.controls.password.valid || loginForm.controls.password.pristine" class="alert alert-danger">
- Password is required
+ <div *ngIf="formErrors.password" class="alert alert-danger">
+ {{ formErrors.password }}
</div>
</div>
- <input type="submit" value="Login" class="btn btn-default" [disabled]="!loginForm.valid">
+ <input type="submit" value="Login" class="btn btn-default" [disabled]="!form.valid">
</form>
import { Component, OnInit } from '@angular/core';
-import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
-import { AuthService } from '../shared';
+import { AuthService, FormReactive } from '../shared';
@Component({
selector: 'my-login',
template: require('./login.component.html')
})
-export class LoginComponent implements OnInit {
+export class LoginComponent extends FormReactive implements OnInit {
error: string = null;
- username = '';
- password: '';
- loginForm: FormGroup;
+
+ form: FormGroup;
+ formErrors = {
+ 'username': '',
+ 'password': ''
+ };
+ validationMessages = {
+ 'username': {
+ 'required': 'Username is required.',
+ },
+ 'password': {
+ 'required': 'Password is required.'
+ }
+ };
constructor(
private authService: AuthService,
+ private formBuilder: FormBuilder,
private router: Router
- ) {}
+ ) {
+ super();
+ }
- ngOnInit() {
- this.loginForm = new FormGroup({
- username: new FormControl('', [ <any>Validators.required ]),
- password: new FormControl('', [ <any>Validators.required ]),
+ buildForm() {
+ this.form = this.formBuilder.group({
+ username: [ '', Validators.required ],
+ password: [ '', Validators.required ],
});
+
+ this.form.valueChanges.subscribe(data => this.onValueChanged(data));
+ }
+
+ ngOnInit() {
+ this.buildForm();
}
login() {
- this.authService.login(this.username, this.password).subscribe(
- result => {
- this.error = null;
+ this.error = null;
+
+ const { username, password } = this.form.value;
+
+ this.authService.login(username, password).subscribe(
+ result => this.router.navigate(['/videos/list']),
- this.router.navigate(['/videos/list']);
- },
error => {
console.error(error.json);
+++ /dev/null
-export * from './url.validator';
+++ /dev/null
-import { FormControl } from '@angular/forms';
-
-export function validateUrl(c: FormControl) {
- let URL_REGEXP = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$');
-
- return URL_REGEXP.test(c.value) ? null : {
- validateUrl: {
- valid: false
- }
- };
-}
--- /dev/null
+import { FormGroup } from '@angular/forms';
+
+export abstract class FormReactive {
+ abstract form: FormGroup;
+ abstract formErrors: Object;
+ abstract validationMessages: Object;
+
+ abstract buildForm(): void;
+
+ protected onValueChanged(data?: any) {
+ for (const field in this.formErrors) {
+ // clear previous error message (if any)
+ this.formErrors[field] = '';
+ const control = this.form.get(field);
+
+ if (control && control.dirty && !control.valid) {
+ const messages = this.validationMessages[field];
+ for (const key in control.errors) {
+ this.formErrors[field] += messages[key] + ' ';
+ }
+ }
+ }
+ }
+}
--- /dev/null
+export * from './url.validator';
+export * from './user';
+export * from './video';
--- /dev/null
+import { FormControl } from '@angular/forms';
+
+export function validateUrl(c: FormControl) {
+ let URL_REGEXP = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$');
+
+ return URL_REGEXP.test(c.value) ? null : {
+ validateUrl: {
+ valid: false
+ }
+ };
+}
--- /dev/null
+import { Validators } from '@angular/forms';
+
+export const USER_USERNAME = {
+ VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ],
+ MESSAGES: {
+ 'required': 'Username is required.',
+ 'minlength': 'Username must be at least 3 characters long.',
+ 'maxlength': 'Username cannot be more than 20 characters long.'
+ }
+};
+export const USER_PASSWORD = {
+ VALIDATORS: [ Validators.required, Validators.minLength(6) ],
+ MESSAGES: {
+ 'required': 'Password is required.',
+ 'minlength': 'Password must be at least 6 characters long.',
+ }
+};
--- /dev/null
+import { Validators } from '@angular/forms';
+
+export const VIDEO_NAME = {
+ VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(50) ],
+ MESSAGES: {
+ 'required': 'Video name is required.',
+ 'minlength': 'Video name must be at least 3 characters long.',
+ 'maxlength': 'Video name cannot be more than 50 characters long.'
+ }
+};
+export const VIDEO_DESCRIPTION = {
+ VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(250) ],
+ MESSAGES: {
+ 'required': 'Video description is required.',
+ 'minlength': 'Video description must be at least 3 characters long.',
+ 'maxlength': 'Video description cannot be more than 250 characters long.'
+ }
+};
+
+export const VIDEO_TAGS = {
+ VALIDATORS: [ Validators.pattern('^[a-zA-Z0-9]{2,10}$') ],
+ MESSAGES: {
+ 'pattern': 'A tag should be between 2 and 10 alphanumeric characters long.'
+ }
+};
--- /dev/null
+export * from './form-validators';
+export * from './form-reactive';
export * from './auth';
-export * from './form-validators';
+export * from './forms';
export * from './rest';
export * from './search';
export * from './users';
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-<form novalidate (ngSubmit)="upload()" [formGroup]="videoForm">
+<form novalidate (ngSubmit)="upload()" [formGroup]="form">
<div class="form-group">
<label for="name">Name</label>
<input
- type="text" class="form-control" name="name" id="name"
- [(ngModel)]="video.name"
+ type="text" class="form-control" id="name"
+ formControlName="name"
>
- <div [hidden]="videoForm.controls.name.valid || videoForm.controls.name.pristine" class="alert alert-warning">
- A name is required and should be between 3 and 50 characters long
+ <div *ngIf="formErrors.name" class="alert alert-danger">
+ {{ formErrors.name }}
</div>
</div>
<div class="form-group">
<label for="tags">Tags</label>
<input
- type="text" class="form-control" name="tags" id="tags"
- [disabled]="isTagsInputDisabled" (keyup)="onTagKeyPress($event)" [(ngModel)]="currentTag"
+ type="text" class="form-control" id="currentTag"
+ formControlName="currentTag" (keyup)="onTagKeyPress($event)"
>
- <div [hidden]="videoForm.controls.tags.valid || videoForm.controls.tags.pristine" class="alert alert-warning">
- A tag should be between 2 and 10 characters (alphanumeric) long
+ <div *ngIf="formErrors.currentTag" class="alert alert-danger">
+ {{ formErrors.currentTag }}
</div>
</div>
<div class="tags">
- <div class="label label-primary tag" *ngFor="let tag of video.tags">
+ <div class="label label-primary tag" *ngFor="let tag of tags">
{{ tag }}
<span class="remove" (click)="removeTag(tag)">x</span>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea
- name="description" id="description" class="form-control" placeholder="Description..."
- [(ngModel)]="video.description"
+ id="description" class="form-control" placeholder="Description..."
+ formControlName="description"
>
</textarea>
- <div [hidden]="videoForm.controls.description.valid || videoForm.controls.description.pristine" class="alert alert-warning">
- A description is required and should be between 3 and 250 characters long
+ <div *ngIf="formErrors.description" class="alert alert-danger">
+ {{ formErrors.description }}
</div>
</div>
<div class="form-group">
<input
type="submit" value="Upload" class="btn btn-default form-control" [title]="getInvalidFieldsTitle()"
- [disabled]="!videoForm.valid || video.tags.length === 0 || filename === null"
+ [disabled]="!form.valid || tags.length === 0 || filename === null"
>
</div>
</form>
import { Component, ElementRef, OnInit } from '@angular/core';
-import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
-import { AuthService } from '../../shared';
+import { AuthService, FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared';
@Component({
selector: 'my-videos-add',
template: require('./video-add.component.html')
})
-export class VideoAddComponent implements OnInit {
- currentTag: string; // Tag the user is writing in the input
- error: string = null;
- videoForm: FormGroup;
+export class VideoAddComponent extends FormReactive implements OnInit {
+ tags: string[] = [];
uploader: FileUploader;
- video = {
+
+ error: string = null;
+ form: FormGroup;
+ formErrors = {
name: '',
- tags: [],
- description: ''
+ description: '',
+ currentTag: ''
+ };
+ validationMessages = {
+ name: VIDEO_NAME.MESSAGES,
+ description: VIDEO_DESCRIPTION.MESSAGES,
+ currentTag: VIDEO_TAGS.MESSAGES
};
constructor(
private authService: AuthService,
private elementRef: ElementRef,
+ private formBuilder: FormBuilder,
private router: Router
- ) {}
+ ) {
+ super();
+ }
get filename() {
if (this.uploader.queue.length === 0) {
return this.uploader.queue[0].file.name;
}
- get isTagsInputDisabled () {
- return this.video.tags.length >= 3;
+ buildForm() {
+ this.form = this.formBuilder.group({
+ name: [ '', VIDEO_NAME.VALIDATORS ],
+ description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
+ currentTag: [ '', VIDEO_TAGS.VALIDATORS ]
+ });
+
+ this.form.valueChanges.subscribe(data => this.onValueChanged(data));
}
getInvalidFieldsTitle() {
let title = '';
- const nameControl = this.videoForm.controls['name'];
- const descriptionControl = this.videoForm.controls['description'];
+ const nameControl = this.form.controls['name'];
+ const descriptionControl = this.form.controls['description'];
if (!nameControl.valid) {
title += 'A name is required\n';
}
- if (this.video.tags.length === 0) {
+ if (this.tags.length === 0) {
title += 'At least one tag is required\n';
}
}
ngOnInit() {
- this.videoForm = new FormGroup({
- name: new FormControl('', [ <any>Validators.required, <any>Validators.minLength(3), <any>Validators.maxLength(50) ]),
- description: new FormControl('', [ <any>Validators.required, <any>Validators.minLength(3), <any>Validators.maxLength(250) ]),
- tags: new FormControl('', <any>Validators.pattern('^[a-zA-Z0-9]{2,10}$'))
- });
-
-
this.uploader = new FileUploader({
authToken: this.authService.getRequestHeaderValue(),
queueLimit: 1,
});
this.uploader.onBuildItemForm = (item, form) => {
- form.append('name', this.video.name);
- form.append('description', this.video.description);
+ const name = this.form.value['name'];
+ const description = this.form.value['description'];
+
+ form.append('name', name);
+ form.append('description', description);
- for (let i = 0; i < this.video.tags.length; i++) {
- form.append(`tags[${i}]`, this.video.tags[i]);
+ for (let i = 0; i < this.tags.length; i++) {
+ form.append(`tags[${i}]`, this.tags[i]);
}
};
+
+ this.buildForm();
}
onTagKeyPress(event: KeyboardEvent) {
+ const currentTag = this.form.value['currentTag'];
+
// Enter press
if (event.keyCode === 13) {
// Check if the tag is valid and does not already exist
if (
- this.currentTag !== '' &&
- this.videoForm.controls['tags'].valid &&
- this.video.tags.indexOf(this.currentTag) === -1
+ currentTag !== '' &&
+ this.form.controls['currentTag'].valid &&
+ this.tags.indexOf(currentTag) === -1
) {
- this.video.tags.push(this.currentTag);
- this.currentTag = '';
+ this.tags.push(currentTag);
+ this.form.patchValue({ currentTag: '' });
+
+ if (this.tags.length >= 3) {
+ this.form.get('currentTag').disable();
+ }
}
}
}
}
removeTag(tag: string) {
- this.video.tags.splice(this.video.tags.indexOf(tag), 1);
+ this.tags.splice(this.tags.indexOf(tag), 1);
}
upload() {