angular2-chapter06

Collecting Data with Forms and Validation

Check code at: https://github.com/wghglory/angular2-fundamental

Using Models for Type Safety

  1. create events/shared/event.model.ts
export interface IEvent {
    id: number
    name: string
    date: Date
    time: string
    price: number
    imageUrl: string
    location?: {
        address: string
        city: string
        country: string
    },
    onlineUrl?: string
    sessions: ISession[]
}

export interface ISession {
    id: number
    name: string
    presenter: string
    duration: number
    level: string
    abstract: string
    voters: string[]
}
  1. export in shared index.ts
export * from './event.service'
+ export * from './event.model'
  1. event.service.ts
+ import { Subject, Observable } from 'rxjs/RX'
+ import { IEvent } from './event.model'

@Injectable()
export class EventService {
+    getEvents(): Observable<IEvent[]> {

        //simulate async loading data operation, return subject. Need to change events-list.component.ts
+        let subject = new Subject<IEvent[]>()
        setTimeout(() => {
            subject.next(EVENTS);
            subject.complete();
        }, 100)

        return subject;
    }

+    getEvent(id: number): IEvent {
        return EVENTS.find(e => e.id === id)
    }
}

+ const EVENTS: IEvent[] = ...
  1. update event:any to event:IEvent in event-detail.component, event-list.component, event-thumbnail.component

Creating Your First Template-based Form

  1. user/login.component.ts
import {Component} from '@angular/core'
import {AuthService} from './auth.service'

@Component({
    templateUrl: 'app/user/login.component.html'
})
export class LoginComponent {

    constructor(private authService: AuthService) { }

    login(formValues) {  ////will be object of userName and password
        this.authService.loginUser(formValues.userName, formValues.password)
    }
}
  1. login.component.html
<h1>Login</h1>
<hr>
<div class="col-md-4">
    <form #loginForm="ngForm" (ngSubmit)="login(loginForm.value)" autocomplete="off">
        <div class="form-group">
            <label for="userName">User Name:</label>
            <input (ngModel)="userName" name="userName" id="userName" type="text" class="form-control" placeholder="User Name..." />
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
            <input (ngModel)="password" name="password" id="password" type="password" class="form-control" placeholder="Password..." />
        </div>

        <button type="submit" class="btn btn-primary">Login</button>
        <button type="button" class="btn btn-default">Cancel</button>
    </form>
</div>
  1. Register component in UserModule
import { NgModule } from '@angular/core'
import { RouterModule } from '@angular/router'
import { CommonModule } from '@angular/common'
import {userRoutes} from './user.routes'
import {ProfileComponent} from './profile.component'
+ import {LoginComponent} from './login.component'
+ import {FormsModule} from '@angular/forms'

@NgModule({
    imports: [
        CommonModule,
+        FormsModule,
        RouterModule.forChild(userRoutes)
    ],
    declarations: [
        ProfileComponent,
+        LoginComponent
    ],
})
export class UserModule { }
  1. Add user.routes.ts path
import {ProfileComponent} from './profile.component'
+ import {LoginComponent} from './login.component'

export const userRoutes = [
    { path: 'profile', component: ProfileComponent },
+    { path: 'login', component: LoginComponent }
]
  1. create user/user.model.ts:
export interface IUser {
    id: number
    firstName: string
    lastName: string
    userName: string
}
  1. create user/auth.service.ts:
import {Injectable} from '@angular/core'
import {IUser} from './user.model'

@Injectable()
export class AuthService {
    currentUser: IUser

    loginUser(userName: string, password: string) {
        this.currentUser = {
            id: 1,
            userName: userName,
            firstName: 'guanghui',
            lastName: 'wang'
        }
    }

    isAuthenticated() {
        return !!this.currentUser;
    }
}
  1. register service in app.module.ts
...
+ import {AuthService} from './user/auth.service'

@NgModule({
    ...
    providers: [
        ...
+        AuthService
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
  1. inject AuthService in nav.component.ts
import {Component} from '@angular/core'
+ import {AuthService} from '../user/auth.service'

@Component({
    selector: 'nav-bar',
    templateUrl: 'app/nav/nav.component.html',
    styles: [`
        .nav.navbar-nav {font-size:15px;}
        #searchForm {margin-right:100px;}
        @media(max-width:1200px) {#searchForm {display:none;}}
        li>a.active{color:#f97924;}
    `]
})
export class NavBarComponent {
+    constructor(private authService: AuthService) { }
}
  1. modify nav.component.html
<li>
    <a *ngIf="!authService.isAuthenticated()" [routerLink]="['user/login']">Login</a>
    <a *ngIf="authService.isAuthenticated()" [routerLink]="['user/profile']">Welcome {{authService.currentUser.userName}}</a>
</li>

Now when you type username, password and then click login, the rightmost area in navbar's "login" will become the userName.

We will finish up the other features below, like when we click login, or cancel, navigate to all events page

So login.component.ts:

import {Component} from '@angular/core'
import {AuthService} from './auth.service'

+ import {Router} from '@angular/router'

@Component({
    templateUrl: 'app/user/login.component.html'
})
export class LoginComponent {

+    constructor(private authService: AuthService, private router:Router) { }

    login(formValues) {
        this.authService.loginUser(formValues.userName, formValues.password)

+        this.router.navigate(['events'])
    }

+    cancel(){
+        this.router.navigate(['events'])
+    }
}

add cancel click in login.component.html

<button type="button" (click)="cancel()" class="btn btn-default">Cancel</button>

Validating Template-based Forms (LoginForm)

login.component.html

<h1>Login</h1>
<hr>
<div class="col-md-4">
+    <form #loginForm="ngForm" (ngSubmit)="login(loginForm.value)" autocomplete="off" novalidate>
        <div class="form-group">
            <label for="userName">User Name:</label>
+            <em *ngIf="loginForm.controls.userName?.invalid && (loginForm.controls.userName?.touched || mouseoverLogin)">Required</em>
+            <input required (ngModel)="userName" name="userName" id="userName" type="text" class="form-control" placeholder="User Name..." />
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
+            <em *ngIf="loginForm.controls.password?.invalid && (loginForm.controls.password?.touched || mouseoverLogin)">Required</em>
+            <input required (ngModel)="password" name="password" id="password" type="password" class="form-control" placeholder="Password..." />
        </div>
+        <span (mouseenter)="mouseoverLogin=true" (mouseleave)="mouseoverLogin=false">
+            <button [disabled]="loginForm.invalid" type="submit" class="btn btn-primary">Login</button>
+        </span>
        <button type="button" (click)="cancel()" class="btn btn-default">Cancel</button>
    </form>
</div>

<!-- hover on the login button, display the reason if user is not allowed login -->
<!-- disabled button cannot fire event! So have to wrap span around button -->

login.component.ts add styles:

@Component({
    templateUrl: 'app/user/login.component.html',
    styles:[
        `em {float:right;color:#e05c65;padding-left:10px;}`
    ]
})

Creating Your First Reactive Form (Profile Form)

  1. create profile.component.html and update profile.component.ts
import { Component, OnInit } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { AuthService } from './auth.service'
import { Router } from '@angular/router'

@Component({
    templateUrl: 'app/user/profile.component.html'
})
export class ProfileComponent implements OnInit {
    constructor(private authService: AuthService, private router: Router) { }

    profileForm: FormGroup

    ngOnInit() {
        let firstName = new FormControl(this.authService.currentUser.firstName)
        let lastName = new FormControl(this.authService.currentUser.lastName)

        this.profileForm = new FormGroup({
            firstName: firstName,
            lastName: lastName
        })
    }

    cancel() {
        this.router.navigate(['events'])
    }

    saveProfile(formValues) {
        this.authService.updateCurrentUser(formValues.firstName, formValues.lastName)

        this.router.navigate(['events'])
    }
}
<div>
    <h1>Edit Your Profile </h1>
    <hr>
    <div class="col-md-4">
        <form [formGroup]="profileForm" (ngSubmit)="saveProfile(profileForm.value)" autocomplete="off" novalidate>
            <div class="form-group">
                <label for="firstName">First Name:</label>
                <input formControlName="firstName" id="firstName" type="text" class="form-control" placeholder="First Name..." />
            </div>
            <div class="form-group">
                <label for="lastName">Last Name:</label>
                <input formControlName="lastName" id="lastName" type="text" class="form-control" placeholder="Last Name..." />
            </div>

            <button type="submit" class="btn btn-primary">Save</button>
            <button (click)="cancel()" type="button" class="btn btn-default">Cancel</button>
        </form>
    </div>
</div>
  1. import ReactiveFormsModule into user.module.ts
...
+ import {FormsModule,ReactiveFormsModule} from '@angular/forms'

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
+        ReactiveFormsModule,
        RouterModule.forChild(userRoutes)
    ],
    ...
})
export class UserModule { }
  1. add updateCurrentUser method in authService
export class AuthService {
    ...

    updateCurrentUser(firstName: string, lastName: string) {
        this.currentUser.firstName = firstName;
        this.currentUser.lastName = lastName;
    }
}

Validating Reactive Forms

  1. add required Validators, error style, validateFirstName, validateLastName functions in profile.component.ts
import { Component, OnInit } from '@angular/core'
+ import { FormControl, FormGroup, Validators } from '@angular/forms'
import { AuthService } from './auth.service'
import { Router } from '@angular/router'

@Component({
    templateUrl: 'app/user/profile.component.html',
+    styles: [
        `
        em {float:right;color:#e05c65;padding-left:10px;}
        .error input{background-color:#e3c3c5;}
        .error ::-webkit-input-placeholder {color:#999;}
        .error ::-moz-placeholder {color:#999;}
        .error :-ms-input-placeholder {color:#999;}
        `
    ]
})
export class ProfileComponent implements OnInit {
    constructor(private authService: AuthService, private router: Router) { }

    profileForm: FormGroup
+   private firstName: FormControl
+   private lastName: FormControl

    ngOnInit() {
+        this.firstName = new FormControl(this.authService.currentUser.firstName, Validators.required)
+        this.lastName = new FormControl(this.authService.currentUser.lastName, Validators.required)

        this.profileForm = new FormGroup({
+            firstName: this.firstName,
+            lastName: this.lastName
        })
    }

    cancel() {
        this.router.navigate(['events'])
    }

    saveProfile(formValues) {
+        if (this.profileForm.valid) {
            this.authService.updateCurrentUser(formValues.firstName, formValues.lastName)
            this.router.navigate(['events'])
        }
    }

+    validateFirstName() {
+        return this.firstName.valid || this.firstName.untouched
+    }

+    validateLastName() {
+        return this.lastName.valid || this.lastName.untouched
+    }
}
  1. update profile template:
<div>
    <h1>Edit Your Profile </h1>
    <hr>
    <div class="col-md-4">
        <form [formGroup]="profileForm" (ngSubmit)="saveProfile(profileForm.value)" autocomplete="off" novalidate>
+            <div class="form-group" [ngClass]="{'error': !validateFirstName()}">
                <label for="firstName">First Name:</label>
+                <em *ngIf="!validateFirstName()">Required</em>
                <input formControlName="firstName" id="firstName" type="text" class="form-control" placeholder="First Name..." />
            </div>
+            <div class="form-group" [ngClass]="{'error': !validateLastName()}">
                <label for="lastName">Last Name:</label>
+                <em *ngIf="!validateLastName()">Required</em>
                <input formControlName="lastName" id="lastName" type="text" class="form-control" placeholder="Last Name..." />
            </div>

            <button type="submit" class="btn btn-primary">Save</button>
            <button (click)="cancel()" type="button" class="btn btn-default">Cancel</button>
        </form>
    </div>
</div>

Using Multiple Validators in Reactive Forms

Let's say we want firstName starts with a letter, so we add Validators.pattern

import { FormControl, FormGroup, Validators } from '@angular/forms'

@Component({
    templateUrl: 'app/user/profile.component.html',
    styles: [
        `
        em {float:right;color:#e05c65;padding-left:10px;}
        .error input{background-color:#e3c3c5;}
        .error ::-webkit-input-placeholder {color:#999;}
        .error ::-moz-placeholder {color:#999;}
        .error :-ms-input-placeholder {color:#999;}
        `
    ]
})
export class ProfileComponent implements OnInit {
    constructor(private authService: AuthService, private router: Router) { }

    profileForm: FormGroup
    firstName: FormControl
    lastName: FormControl

    ngOnInit() {
+        this.firstName = new FormControl(this.authService.currentUser.firstName, [Validators.required, Validators.pattern('[a-zA-Z].*')])
        this.lastName = new FormControl(this.authService.currentUser.lastName, Validators.required)

        this.profileForm = new FormGroup({
            firstName: this.firstName,
            lastName: this.lastName
        })
    }
    ...
}

profile template:

<div>
    <h1>Edit Your Profile </h1>
    <hr>
    <div class="col-md-4">
        <form [formGroup]="profileForm" (ngSubmit)="saveProfile(profileForm.value)" autocomplete="off" novalidate>
            <div class="form-group" [ngClass]="{'error': !validateFirstName()}">
                <label for="firstName">First Name:</label>
+                <em *ngIf="!validateFirstName() && profileForm.controls.firstName.errors.required">Required</em>
+                <em *ngIf="!validateFirstName() && profileForm.controls.firstName.errors.pattern">firstName starts with a letter</em>
                <input formControlName="firstName" id="firstName" type="text" class="form-control" placeholder="First Name..." />
            </div>
            ...
        </form>
    </div>
</div>

Diving Deeper into Template-based Forms (Create New Event)

  1. add create-event.component.html

  2. create-event.component.ts: Add styles, saveEvent

//click cancel will use router to navigate to /events (load all events)
import {Component} from '@angular/core'
import {Router} from '@angular/router'
import {EventService} from './shared/event.service'

@Component({
    templateUrl: 'app/events/create-event.component.html',
    styles: [
        `
        em {float:right;color:#e05c65;padding-left:10px;}
        .error input{background-color:#e3c3c5;}
        .error ::-webkit-input-placeholder {color:#999;}
        .error ::-moz-placeholder {color:#999;}
        .error :-ms-input-placeholder {color:#999;}
        `
    ]
})
export class CreateEventComponent {
    isDirty: boolean = true

    constructor(private router: Router, private eventService: EventService) { }

    cancel() {
        this.router.navigate(['/events'])
    }

    saveEvent(formValues) {
        this.eventService.saveEvent(formValues)
        this.isDirty = false
        this.router.navigate(['/events'])
    }
}

we can now add a temporary method in event.service.ts

saveEvent(event){
    event.id = 999;
    event.sessions = [];
    EVENTS.push(event);
}
  1. import FormsModule, ReactiveFormsModule in app.module.ts

  2. now when we submit the form, formValues is a object that has city, address, country, name, etc. They are at same level. Our model structure actually has location property which includes address, city, country. So we can use ngModelGroup to wrap up these 3 fields.

+ <div ngModelGroup="location">
    <div class="form-group">
        <label for="address">Event Location:</label>
        <input (ngModel)="address" name="address" id="address" type="text" class="form-control" placeholder="Address of event..." />
    </div>
    <div class="row">
        <div class="col-md-6">
            <input (ngModel)="city" name="city" id="city" type="text" class="form-control" placeholder="City..." />
        </div>
        <div class="col-md-6">
            <input (ngModel)="country" name="country" id="country" type="text" class="form-control" placeholder="Country..." />
        </div>
    </div>
+ </div>

data is like {name: "fdsa", date: "1010/2/2", time: "8:00", price: 34432, location: {address: "fds", city: "fdasfdas", country: "fdsa"}, …}

Diving Deeper into Reactive Forms (Event-detail shows session)

  1. create /events/event-detail/create-session.component.ts, its template. Don't forget to export in index.ts and register in app.module.ts
import { Component, OnInit } from '@angular/core'
import { FormControl, FormGroup, Validators } from '@angular/forms'
import { ISession } from '../shared/index'

@Component({
    templateUrl: 'app/events/event-detail/create-session.component.html',
    styles: [
        `
        em {float:right;color:#e05c65;padding-left:10px;}
        .error input, .error select, .error textarea {background-color:#e3c3c5;}
        .error ::-webkit-input-placeholder {color:#999;}
        .error ::-moz-placeholder {color:#999;}
        .error :-ms-input-placeholder {color:#999;}
        `
    ]
})
export class CreateSessionComponent implements OnInit {
    newSessionForm: FormGroup
    name: FormControl
    presenter: FormControl
    duration: FormControl
    level: FormControl
    abstract: FormControl

    ngOnInit() {
        this.name = new FormControl('', Validators.required)
        this.presenter = new FormControl('', Validators.required)
        this.duration = new FormControl('', Validators.required)
        this.level = new FormControl('', Validators.required)
        this.abstract = new FormControl('', [Validators.required, Validators.maxLength(400)])

        this.newSessionForm = new FormGroup({
            name: this.name,
            presenter: this.presenter,
            duration: this.duration,
            level: this.level,
            abstract: this.abstract
        })
    }

    saveSession(formValues) {
        // console.log(formValues);

        let session: ISession = {
            id: undefined,
            name: formValues.name,
            duration: +formValues.duration,  //convert
            presenter: formValues.presenter,
            level: formValues.level,
            abstract: formValues.abstract,
            voters: []
        }
        console.log(session)
    }
}
<div class="col-md-12">
    <h3>Create Session</h3>
</div>
<div class="col-md-6">
    <form [formGroup]="newSessionForm" (ngSubmit)="saveSession(newSessionForm.value)" autocomplete="off">
        <div class="form-group" [ngClass]="{'error': name.invalid && name.dirty}">
            <label for="sessionName">Session Name:</label>
            <em *ngIf="name.invalid && name.dirty">Required</em>
            <input formControlName="name" id="sessionName" type="text" class="form-control" placeholder="session name..." />
        </div>
        <div class="form-group" [ngClass]="{'error': presenter.invalid && presenter.dirty}">
            <label for="presenter">Presenter:</label>
            <em *ngIf="presenter.invalid && presenter.dirty">Required</em>
            <input formControlName="presenter" id="presenter" type="text" class="form-control" placeholder="presenter..." />
        </div>
        <div class="form-group" [ngClass]="{'error': duration.invalid && duration.dirty}">
            <label for="duration">Duration:</label>
            <em *ngIf="duration.invalid && duration.dirty">Required</em>
            <select formControlName="duration" class="form-control">
                <option value="">select duration...</option>
                <option value="1">Half Hour</option>
                <option value="2">1 Hour</option>
                <option value="3">Half Day</option>
                <option value="4">Full Day</option>
            </select>
        </div>
        <div class="form-group" [ngClass]="{'error': level.invalid && level.dirty}">
            <label for="level">Level:</label>
            <em *ngIf="level.invalid && level.dirty">Required</em>
            <select formControlName="level" class="form-control">
                <option value="">select level...</option>
                <option value="Beginner">Beginner</option>
                <option value="Intermediate">Intermediate</option>
                <option value="Advanced">Advanced</option>
            </select>
        </div>
        <div class="form-group" [ngClass]="{'error': abstract.invalid && abstract.dirty}">
            <label for="abstract">Abstract:</label>
            <em *ngIf="abstract.invalid && abstract.dirty && abstract?.errors.required">Required</em>
            <em *ngIf="abstract.invalid && abstract.dirty && abstract?.errors.maxlength">Cannot exceed 400 characters</em>
            <textarea formControlName="abstract" id="abstract" rows="3" class="form-control" placeholder="abstract..."></textarea>
        </div>
        <button type="submit" [disabled]="newSessionForm.invalid" class="btn btn-primary">Save</button>
        <button type="button" class="btn btn-default">Cancel</button>
    </form>
</div>
  1. register it in routes.ts
import {CreateSessionComponent} from './events/event-detail/create-session.component'

export const appRoutes: Routes = [
    //order matters
    { path: 'events/new', component: CreateEventComponent, canDeactivate: ['canDeactivateCreateEvent'] }, //Guarding Against Route De-activation using function, canDeactivateCreateEvent is provider name which points to a function
    { path: 'events', component: EventsListComponent, resolve: { events1: EventListResolver } }, //call EventListResolver before using the component, bind resolver result to a property events1, and this property will be passed to the component
    { path: 'events/:id', component: EventDetailComponent, canActivate: [EventRouteActivator] }, //Guarding Against Route Activation using service
+    { path: 'events/session/new', component: CreateSessionComponent },
    { path: '404', component: Error404Component },
    { path: '', redirectTo: '/events', pathMatch: 'full' },  //pathMatch: prefix or full

    // user prefix, localhost/user/x, will load module here: app/user/user.module and the module name is UserModule, concat '#'
    { path: 'user', loadChildren: 'app/user/user.module#UserModule' }
]

Custom Validation

create-session.component.ts

import { Component, OnInit } from '@angular/core'
import { FormControl, FormGroup, Validators } from '@angular/forms'
import { ISession, restrictedWords } from '../shared/index'

@Component({
    templateUrl: 'app/events/event-detail/create-session.component.html',
    styles: [
        `
        em {float:right;color:#e05c65;padding-left:10px;}
        .error input, .error select, .error textarea {background-color:#e3c3c5;}
        .error ::-webkit-input-placeholder {color:#999;}
        .error ::-moz-placeholder {color:#999;}
        .error :-ms-input-placeholder {color:#999;}
        `
    ]
})
export class CreateSessionComponent implements OnInit {
    newSessionForm: FormGroup
    name: FormControl
    presenter: FormControl
    duration: FormControl
    level: FormControl
    abstract: FormControl

    ngOnInit() {
        this.name = new FormControl('', Validators.required)
        this.presenter = new FormControl('', Validators.required)
        this.duration = new FormControl('', Validators.required)
        this.level = new FormControl('', Validators.required)
        this.abstract = new FormControl('', [Validators.required, Validators.maxLength(400), restrictedWords(['foo', 'bar'])])

        this.newSessionForm = new FormGroup({
            name: this.name,
            presenter: this.presenter,
            duration: this.duration,
            level: this.level,
            abstract: this.abstract
        })
    }

    saveSession(formValues) {
        // console.log(formValues);

        let session: ISession = {
            id: undefined,
            name: formValues.name,
            duration: +formValues.duration,  //convert
            presenter: formValues.presenter,
            level: formValues.level,
            abstract: formValues.abstract,
            voters: []
        }
        console.log(session)
    }

    //custom validation
    // private restrictedWords(control: FormControl): { [key: string]: any } {
    //     return control.value.includes('foo')
    //         ? { 'restrictedWords': 'foo' }
    //         : null
    // }

    // this will be put into shared folder
    // private restrictedWords(words) {
    //     return (control: FormControl): { [key: string]: any } => {
    //
    //         if (!words) return null;
    //
    //         var invalidWords = words.map(w => control.value.includes(w) ? w : null)
    //             .filter(w => w != null);
    //
    //         return invalidWords && invalidWords.length > 0
    //             ? { 'restrictedWords': invalidWords.join(', ') }
    //             : null
    //     }
    // }
}

shared/restricted-words.validator.ts, and don't forget export in index.ts barrel

import {FormControl} from '@angular/forms'

export function restrictedWords(words) {
    return (control: FormControl): { [key: string]: any } => {

        if (!words) return null;

        var invalidWords = words.map(w => control.value.includes(w) ? w : null)
            .filter(w => w != null);

        return invalidWords && invalidWords.length > 0
            ? { 'restrictedWords': invalidWords.join(', ') }
            : null
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 最近一周明显感觉自己的执行力太差,很多东西不能马上着手解决,而是拖拖拖拖拖拖。这个毛病养成已久,不过愈发觉得他需要...
    一七一七阅读 279评论 0 0
  • 当那个男孩儿在凉风紧握着我的手,一把将我紧紧抱住,我感受到了埋在我脖颈处的呼吸很急促,甚至一些湿湿的东西在那...
    独角指向阅读 796评论 0 2
  • 01 很多刚参加工作的人都试图和同事建立如同同学一般的关系,并有意无意的在工作中试图找到和在学校一样的感觉。特别是...
    恣意生活阅读 933评论 9 41