diff --git a/src/app/app.component.html b/src/app/app.component.html index 532d4b3..16768ec 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,14 +1,9 @@ -
-

+
+

Welcome to {{title}}!

-
-
- - -
-
+
\ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 341343b..7ae2166 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,6 +7,7 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.css'] }) export class AppComponent { + title = "Angular 2"; task = { title: 'Review Applications', assignee: null diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 92b46e5..f12a77d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,8 +1,12 @@ +import { ErrorHandler } from '@angular/core'; +import { AppErrorHandler } from './common/app-error-handler'; +import { PostService } from './services/post.service'; import { SummaryPipe } from './summary.pipe'; import { CoursesService } from './course.service'; import { CoursesComponent } from './courses.component'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { HttpModule } from '@angular/http'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { CourseComponent } from './course/course.component'; @@ -11,6 +15,9 @@ import { PanelComponent } from './panel/panel.component'; import { InputFormatDirective } from './input-format.directive'; import { ContactFormComponent } from './contact-form/contact-form.component'; import { SignupFormComponent } from './signup-form/signup-form.component'; +import { ChangePasswordFormComponent } from "./assignments/change-password-form/change-password-form.component"; +import { PostsComponent } from './posts/posts.component'; + @NgModule({ declarations: [ @@ -22,15 +29,21 @@ import { SignupFormComponent } from './signup-form/signup-form.component'; PanelComponent, InputFormatDirective, ContactFormComponent, - SignupFormComponent + SignupFormComponent, + ChangePasswordFormComponent, + PostsComponent ], imports: [ BrowserModule, FormsModule, - ReactiveFormsModule + ReactiveFormsModule, + HttpModule ], providers: [ - CoursesService + CoursesService, + PostService, + AppErrorHandler, + { provide: ErrorHandler, useClass: AppErrorHandler} ], bootstrap: [AppComponent] }) diff --git a/src/app/assignments/change-password-form/change-password-form.component.css b/src/app/assignments/change-password-form/change-password-form.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/assignments/change-password-form/change-password-form.component.html b/src/app/assignments/change-password-form/change-password-form.component.html new file mode 100644 index 0000000..0fabba2 --- /dev/null +++ b/src/app/assignments/change-password-form/change-password-form.component.html @@ -0,0 +1,36 @@ +
+
+ + +
+
Old Password is required
+
Old Password is invalid.
+
+
+ Checking valid password... +
+
+
+ + +
+
New password is required
+
+ +
+ +
+ + +
+
+ Confirm password is required +
+
+
+ Password do not match. +
+
+ + +
\ No newline at end of file diff --git a/src/app/assignments/change-password-form/change-password-form.component.spec.ts b/src/app/assignments/change-password-form/change-password-form.component.spec.ts new file mode 100644 index 0000000..5890b77 --- /dev/null +++ b/src/app/assignments/change-password-form/change-password-form.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChangePasswordFormComponent } from './change-password-form.component'; + +describe('ChangePasswordFormComponent', () => { + let component: ChangePasswordFormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChangePasswordFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChangePasswordFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/assignments/change-password-form/change-password-form.component.ts b/src/app/assignments/change-password-form/change-password-form.component.ts new file mode 100644 index 0000000..bd8d5cb --- /dev/null +++ b/src/app/assignments/change-password-form/change-password-form.component.ts @@ -0,0 +1,35 @@ +import { PasswordValidators } from './password.validators'; +import { FormBuilder, Validators, FormGroup } from '@angular/forms'; +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'change-password-form', + templateUrl: './change-password-form.component.html', + styleUrls: ['./change-password-form.component.css'] +}) +export class ChangePasswordFormComponent { + form: FormGroup; + constructor(private fb: FormBuilder) { + this.form = fb.group({ + oldPassword: ['', + Validators.required, + PasswordValidators.validOldPassword], + + newPassword: ['', Validators.required], + confirmPassword: ['', Validators.required] + }, { + validator: PasswordValidators.passwordShouldMatch + }); + } + + get newPassword(){ + return this.form.get('newPassword'); + } + get oldPassword(){ + return this.form.get('oldPassword'); + } + get confirmPassword(){ + return this.form.get('confirmPassword'); + } + +} diff --git a/src/app/assignments/change-password-form/password.validators.ts b/src/app/assignments/change-password-form/password.validators.ts new file mode 100644 index 0000000..6f7b202 --- /dev/null +++ b/src/app/assignments/change-password-form/password.validators.ts @@ -0,0 +1,24 @@ +import { ValidationErrors, AbstractControl } from '@angular/forms'; +export class PasswordValidators { + + static validOldPassword(control: AbstractControl) { + return new Promise((resolve) => { + setTimeout(() => { + if (control.value !== '1212') { + resolve({ invalidOldPassword: true }) + } + else + resolve(null); + }, 2000); + }); + } + + static passwordShouldMatch(control: AbstractControl) { + let newPassword = control.get('newPassword'); + let confirmPassword = control.get('confirmPassword'); + if (newPassword.value !== confirmPassword.value) + return { passwordShouldMatch: true }; + else + return null; + } +} \ No newline at end of file diff --git a/src/app/common/app-error-handler.ts b/src/app/common/app-error-handler.ts new file mode 100644 index 0000000..ec8e833 --- /dev/null +++ b/src/app/common/app-error-handler.ts @@ -0,0 +1,9 @@ +import { ErrorHandler } from "@angular/core"; + +export class AppErrorHandler implements ErrorHandler{ + handleError(error: any): void { + alert('An expected error occurred.'); + console.log(error); + } + +} \ No newline at end of file diff --git a/src/app/common/app-error.ts b/src/app/common/app-error.ts new file mode 100644 index 0000000..c95295f --- /dev/null +++ b/src/app/common/app-error.ts @@ -0,0 +1,5 @@ +export class AppError{ + constructor(public originalError?: any){ + + } +} \ No newline at end of file diff --git a/src/app/common/bad-request-error.ts b/src/app/common/bad-request-error.ts new file mode 100644 index 0000000..57eb17f --- /dev/null +++ b/src/app/common/bad-request-error.ts @@ -0,0 +1,4 @@ +import { AppError } from './app-error'; +export class BadRequestError extends AppError { + +} \ No newline at end of file diff --git a/src/app/common/not-found-error.ts b/src/app/common/not-found-error.ts new file mode 100644 index 0000000..729b423 --- /dev/null +++ b/src/app/common/not-found-error.ts @@ -0,0 +1,4 @@ +import { AppError } from './app-error'; +export class NotFoundError extends AppError{ + +} \ No newline at end of file diff --git a/src/app/course/course.component.html b/src/app/course/course.component.html index e8f7228..10b1cb7 100644 --- a/src/app/course/course.component.html +++ b/src/app/course/course.component.html @@ -1,8 +1,31 @@ -
- -
    -
  • - {{ topic.value }} -
  • -
+ +
+ + + +
Name is is required
+
Name should be minumum 3 characters.
+
Name is already taken.
+ +
    +
  • + {{ topic }} +
  • +
+
+
+
+ + +
Email is is required
+
Email format is not valid
+
+
+ + +
Phone is is required
+
Phone format is not valid
+
+
+
\ No newline at end of file diff --git a/src/app/course/course.component.ts b/src/app/course/course.component.ts index 230e398..61fb1fb 100644 --- a/src/app/course/course.component.ts +++ b/src/app/course/course.component.ts @@ -1,4 +1,5 @@ -import { FormGroup, FormArray, FormControl } from '@angular/forms'; +import { UsernameValidators } from './../signup-form/username.validators'; +import { FormGroup, FormArray, FormBuilder, Validators } from '@angular/forms'; import { Component, OnInit } from '@angular/core'; @Component({ @@ -7,18 +8,23 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./course.component.css'] }) export class CourseComponent { - form = new FormGroup({ - topics: new FormArray([]) - }); - addTopic(topic: HTMLInputElement) { - this.topics.push(new FormControl(topic.value)); - topic.value = ""; + form: FormGroup; + constructor(fb: FormBuilder) { + this.form = fb.group({ + name: ['', [Validators.required, Validators.minLength(3)], UsernameValidators.shouldBeUnique], + contact: fb.group({ + email: ['', [Validators.required, Validators.pattern('^[a-z]+[a-z0-9._]+@[a-z]+\.[a-z.]{2,5}$')]], + phone: ['', [Validators.required, Validators.pattern('^[0-9]*$')]] + }), + topics: fb.array([]) + }); } - removeTopic(topic: FormControl){ - let index = this.topics.controls.indexOf(topic); - this.topics.removeAt(index); + + addTopic(name) { + this.form.value.topics.push(name.value); } - get topics(){ - return this.form.get('topics') as FormArray; + removeTopic(topic) { + let index = this.form.value.topics.indexOf(topic); + this.form.value.topics.splice(index, 1); } } diff --git a/src/app/posts/posts.component.css b/src/app/posts/posts.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/posts/posts.component.html b/src/app/posts/posts.component.html new file mode 100644 index 0000000..06ca2b9 --- /dev/null +++ b/src/app/posts/posts.component.html @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + + + + +
IdTitle#
{{ p.id }}{{ p.title }} + + +
+
\ No newline at end of file diff --git a/src/app/posts/posts.component.spec.ts b/src/app/posts/posts.component.spec.ts new file mode 100644 index 0000000..ff74bdb --- /dev/null +++ b/src/app/posts/posts.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PostsComponent } from './posts.component'; + +describe('PostsComponent', () => { + let component: PostsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PostsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PostsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/posts/posts.component.ts b/src/app/posts/posts.component.ts new file mode 100644 index 0000000..00c9cb5 --- /dev/null +++ b/src/app/posts/posts.component.ts @@ -0,0 +1,61 @@ +import { AppError } from './../common/app-error'; +import { BadRequestError } from './../common/bad-request-error'; +import { NotFoundError } from './../common/not-found-error'; +import { PostService } from './../services/post.service'; +import { Component, OnInit } from '@angular/core'; +import { Http } from "@angular/http"; + +@Component({ + selector: 'posts', + templateUrl: './posts.component.html', + styleUrls: ['./posts.component.css'] +}) +export class PostsComponent implements OnInit { + ngOnInit(): void { + this.service.getAll().subscribe(posts => this.posts = posts); + } + + posts: any[]; + + + constructor(private service: PostService) { + + } + + createPost(input: HTMLInputElement) { + let post: any = { title: input.value }; + input.value = ''; + this.posts.splice(0, 0, post); + + this.service.create(post).subscribe(posts => { + post['id'] = posts.id; + }, (error: AppError) => { + this.posts.splice(0, 1); + if (error instanceof BadRequestError) { + // this.form.setErrors(error.originalError); + } + else + throw error; + }); + } + + updatePost(post: any) { + this.service.update(post) + .subscribe(updatedPost => { + console.log(updatedPost); + }); + } + + deletePost(post: any) { + let index = this.posts.indexOf(post); + this.posts.splice(index, 1); + this.service.delete(post.id) + .subscribe(null, (error: AppError) => { + this.posts.splice(index, 0, post); + if (error instanceof NotFoundError) + alert('This post has already been deleted.'); + else throw error; + }); + } + +} diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts new file mode 100644 index 0000000..c63308c --- /dev/null +++ b/src/app/services/data.service.ts @@ -0,0 +1,47 @@ +import { BadRequestError } from './../common/bad-request-error'; +import { NotFoundError } from './../common/not-found-error'; +import { AppError } from './../common/app-error'; +import { Http } from '@angular/http'; +import { Injectable } from '@angular/core'; +import "rxjs/add/operator/catch"; +import "rxjs/add/operator/map"; +import "rxjs/add/observable/throw"; +import { Observable } from "rxjs/Observable"; + +@Injectable() +export class DataService { + + constructor(private url: string, private http: Http) { + + } + getAll() { + return this.http.get(this.url) + .map(response => response.json()) + .catch(this.handleError); + } + + create(resource) { + //return Observable.throw(new AppError()); + return this.http.post(this.url, JSON.stringify(resource)) + .map(response => response.json()) + .catch(this.handleError); + } + update(resource) { + return this.http.patch(this.url + '/' + resource.id, JSON.stringify({ isRead: true })) + .catch(this.handleError); + } + delete(id) { + return Observable.throw(new AppError()); + // return this.http.delete(this.url + '/' + id) + // .catch(this.handleError); + } + private handleError(error: Response) { + if (error.status === 400) + return Observable.throw(new BadRequestError(error.json())); + + if (error.status === 404) + return Observable.throw(new NotFoundError()); + else + return Observable.throw(new AppError(error)); + } +} diff --git a/src/app/services/post.service.spec.ts b/src/app/services/post.service.spec.ts new file mode 100644 index 0000000..b19a88e --- /dev/null +++ b/src/app/services/post.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { PostService } from './post.service'; + +describe('PostService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [PostService] + }); + }); + + it('should be created', inject([PostService], (service: PostService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/services/post.service.ts b/src/app/services/post.service.ts new file mode 100644 index 0000000..73c98b4 --- /dev/null +++ b/src/app/services/post.service.ts @@ -0,0 +1,11 @@ +import { Http } from '@angular/http'; +import { DataService } from './data.service'; +import { Injectable } from '@angular/core'; + +@Injectable() +export class PostService extends DataService { + + constructor(http: Http) { + super('http://jsonplaceholder.typicode.com/posts', http); + } +} diff --git a/src/styles.css b/src/styles.css index 9df75fe..2952200 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,6 +1,11 @@ /* You can add global styles to this file, and also import other style files */ @import "~bootstrap/dist/css/bootstrap.css"; +#main-wrapper { + margin: 0 auto; + width: 50%; +} + .form-control.ng-invalid.ng-touched { border: 1px solid #a94442; } \ No newline at end of file