Angular 4 動(dòng)態(tài)表單

本文將介紹如何動(dòng)態(tài)創(chuàng)建表單組件钉寝,我們最終實(shí)現(xiàn)的效果如下:

angular-dynamic-form

在閱讀本文之前弧呐,請(qǐng)確保你已經(jīng)掌握 Angular 響應(yīng)式表單和動(dòng)態(tài)創(chuàng)建組件的相關(guān)知識(shí),如果對(duì)相關(guān)知識(shí)還不了解嵌纲,推薦先閱讀一下 Angular 4 Reactive FormsAngular 4 動(dòng)態(tài)創(chuàng)建組件 這兩篇文章俘枫。對(duì)于已掌握的讀者,我們直接進(jìn)入主題逮走。

創(chuàng)建動(dòng)態(tài)表單

創(chuàng)建 DynamicFormModule

在當(dāng)前目錄先創(chuàng)建 dynamic-form 目錄鸠蚪,然后在該目錄下創(chuàng)建 dynamic-form.module.ts 文件,文件內(nèi)容如下:

dynamic-form/dynamic-form.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    CommonModule,
    ReactiveFormsModule
  ]
})
export class DynamicFormModule {}

創(chuàng)建完 DynamicFormModule 模塊,接著我們需要在 AppModule 中導(dǎo)入該模塊:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { DynamicFormModule } from './dynamic-form/dynamic-form.module';

import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule, DynamicFormModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

創(chuàng)建 DynamicForm 容器

進(jìn)入 dynamic-form 目錄茅信,在創(chuàng)建完 containers 目錄后酣栈,繼續(xù)創(chuàng)建 dynamic-form 目錄,然后在該目錄創(chuàng)建一個(gè)名為 dynamic-form.component.ts 的文件汹押,文件內(nèi)容如下:

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'dynamic-form',
  template: `
    <form [formGroup]="form">
    </form>
  `
})
export class DynamicFormComponent implements OnInit {
  @Input()
  config: any[] = [];

  form: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.createGroup();
  }

  createGroup() {
    const group = this.fb.group({});
    this.config.forEach(control => group.addControl(control.name, this.fb.control('')));
    return group;
  }
}

由于我們的表單是動(dòng)態(tài)的矿筝,我們需要接受一個(gè)數(shù)組類型的配置對(duì)象才能知道需要?jiǎng)討B(tài)創(chuàng)建的內(nèi)容。因此棚贾,我們定義了一個(gè) config 輸入屬性窖维,用于接收數(shù)組類型的配置對(duì)象。

此外我們利用了 Angular 響應(yīng)式表單妙痹,提供的 API 動(dòng)態(tài)的創(chuàng)建 FormGroup 對(duì)象铸史。對(duì)于配置對(duì)象中的每一項(xiàng),我們要求該項(xiàng)至少包含兩個(gè)屬性怯伊,即 (type) 類型和 (name) 名稱:

  • type - 用于設(shè)置表單項(xiàng)的類型琳轿,如 inputselect耿芹、button
  • name - 用于設(shè)置表單控件的 name 屬性

createGroup() 方法中崭篡,我們循環(huán)遍歷輸入的 config 屬性,然后利用 FormGroup 對(duì)象提供的 addControl() 方法吧秕,動(dòng)態(tài)地添加新建的表單控件琉闪。

接下來我們?cè)?DynamicFormModule 模塊中聲明并導(dǎo)出新建的 DynamicFormComponent 組件:

import { DynamicFormComponent } from './containers/dynamic-form/dynamic-form.component';

@NgModule({
  imports: [
    CommonModule,
    ReactiveFormsModule
  ],
  declarations: [
    DynamicFormComponent
  ],
  exports: [
    DynamicFormComponent
  ]
})
export class DynamicFormModule {}

現(xiàn)在我們已經(jīng)創(chuàng)建了表單,讓我們實(shí)際使用它砸彬。

使用動(dòng)態(tài)表單

打開 app.component.ts 文件颠毙,在組件模板中引入我們創(chuàng)建的 dynamic-form 組件,并設(shè)置相關(guān)的配置對(duì)象砂碉,具體示例如下:

app.component.ts

import { Component } from '@angular/core';

interface FormItemOption {
  type: string;
  label: string;
  name: string;
  placeholder?: string;
  options?: string[]
}

@Component({
  selector: 'exe-app',
  template: `
   <div>
     <dynamic-form [config]="config"></dynamic-form>
   </div>
  `
})
export class AppComponent {
  config: FormItemOption[] = [
    {
      type: 'input',
      label: 'Full name',
      name: 'name',
      placeholder: 'Enter your name'
    },
    {
      type: 'select',
      label: 'Favourite food',
      name: 'food',
      options: ['Pizza', 'Hot Dogs', 'Knakworstje', 'Coffee'],
      placeholder: 'Select an option'
    },
    {
      type: 'button',
      label: 'Submit',
      name: 'submit'
    }
  ];
}

上面代碼中蛀蜜,我們?cè)?AppComponent 組件類中設(shè)置了 config 配置對(duì)象,該配置對(duì)象中設(shè)置了三種類型的表單類型增蹭。對(duì)于每個(gè)表單項(xiàng)的配置對(duì)象滴某,我們定義了一個(gè) FormItemOption 數(shù)據(jù)接口,該接口中我們定義了三個(gè)必選屬性:type沪铭、label 和 name 及兩個(gè)可選屬性:options 和 placeholder壮池。下面讓我們創(chuàng)建對(duì)應(yīng)類型的組件偏瓤。

自定義表單項(xiàng)組件

FormInputComponent

dynamic-form 目錄杀怠,我們新建一個(gè) components 目錄,然后創(chuàng)建 form-input厅克、form-selectform-button 三個(gè)文件夾赔退。創(chuàng)建完文件夾后,我們先來定義 form-input 組件:

form-input.component.ts

import { Component, ViewContainerRef } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    selector: 'form-input',
    template: `
    <div [formGroup]="group">
      <label>{{ config.label }}</label>
      <input
        type="text"
        [attr.placeholder]="config.placeholder"
        [formControlName]="config.name" />
    </div>
  `
})
export class FormInputComponent {
    config: any;
    group: FormGroup;
}

上面代碼中,我們?cè)?FormInputComponent 組件類中定義了 configgroup 兩個(gè)屬性硕旗,但我們并沒有使用 @Input 裝飾器來定義它們窗骑,因?yàn)槲覀儾粫?huì)以傳統(tǒng)的方式來使用這個(gè)組件。接下來漆枚,我們來定義 selectbutton 組件创译。

FormSelectComponent

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'form-select',
  template: `
    <div [formGroup]="group">
      <label>{{ config.label }}</label>
      <select [formControlName]="config.name">
        <option value="">{{ config.placeholder }}</option>
        <option *ngFor="let option of config.options">
          {{ option }}
        </option>
      </select>
    </div>
  `
})
export class FormSelectComponent {
  config: Object;
  group: FormGroup;
}

FormSelectComponent 組件與 FormInputComponent 組件的主要區(qū)別是,我們需要循環(huán)配置中定義的options屬性墙基。這用于向用戶顯示所有的選項(xiàng)软族,我們還使用占位符屬性,作為默認(rèn)的選項(xiàng)残制。

FormButtonComponent

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'form-button',
  template: `
    <div [formGroup]="group">
      <button type="submit">
        {{ config.label }}
      </button>
    </div>
  `
})
export class FormButtonComponent{
  config: Object;
  group: FormGroup;
}

以上代碼立砸,我們只是定義了一個(gè)簡(jiǎn)單的按鈕,它使用 config.label 的值作為按鈕文本初茶。與所有組件一樣颗祝,我們需要在前面創(chuàng)建的模塊中聲明這些自定義組件。打開 dynamic-form.module.ts 文件并添加相應(yīng)聲明:

// ...
import { FormButtonComponent } from './components/form-button/form-button.component';
import { FormInputComponent } from './components/form-input/form-input.component';
import { FormSelectComponent } from './components/form-select/form-select.component';

@NgModule({
  // ...
  declarations: [
    DynamicFormComponent,
    FormButtonComponent,
    FormInputComponent,
    FormSelectComponent
  ],
  exports: [
    DynamicFormComponent
  ]
})
export class DynamicFormModule {}

到目前為止恼布,我們已經(jīng)創(chuàng)建了三個(gè)組件螺戳。若想動(dòng)態(tài)的創(chuàng)建這三個(gè)組件,我們將定義一個(gè)指令折汞,該指令的功能跟 router-outlet 指令類似温峭。接下來在 components 目錄內(nèi)部,我們新建一個(gè) dynamic-field 目錄字支,然后創(chuàng)建 dynamic-field.directive.ts 文件凤藏。該文件的內(nèi)容如下:

import { Directive, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Directive({
  selector: '[dynamicField]'
})
export class DynamicFieldDirective {
  @Input()
  config: Object;

  @Input()
  group: FormGroup;
}

我們將指令的 selector 屬性設(shè)置為 [dynamicField],因?yàn)槲覀儗⑵鋺?yīng)用為屬性而不是元素堕伪。

這樣做的好處是揖庄,我們的指令可以應(yīng)用在 Angular 內(nèi)置的 <ng-container> 指令上。<ng-container> 是一個(gè)邏輯容器欠雌,可用于對(duì)節(jié)點(diǎn)進(jìn)行分組蹄梢,但不作為 DOM 樹中的節(jié)點(diǎn),它將被渲染為 HTML中的 comment 元素富俄。因此配合 <ng-container> 指令禁炒,我們只會(huì)在 DOM 中看到我們自定義的組件,而不會(huì)看到 <dynamic-field> 元素 (因?yàn)?DynamicFieldDirective 指令的 selector 被設(shè)置為 [dynamicField] )霍比。

另外在指令中幕袱,我們使用 @Input 裝飾器定義了兩個(gè)輸入屬性,用于動(dòng)態(tài)設(shè)置 configgroup 對(duì)象悠瞬。接下來我們開始動(dòng)態(tài)渲染組件们豌。

動(dòng)態(tài)渲染組件涯捻,我們需要用到 ComponentFactoryResolverViewContainerRef 兩個(gè)對(duì)象。ComponentFactoryResolver 對(duì)象用于創(chuàng)建對(duì)應(yīng)類型的組件工廠 (ComponentFactory)望迎,而 ViewContainerRef 對(duì)象用于表示一個(gè)視圖容器障癌,可添加一個(gè)或多個(gè)視圖,通過它我們可以方便地創(chuàng)建和管理內(nèi)嵌視圖或組件視圖辩尊。

讓我們?cè)?DynamicFieldDirective 指令構(gòu)造函數(shù)中涛浙,注入相關(guān)對(duì)象,具體代碼如下:

import { ComponentFactoryResolver, Directive, Input, OnInit, 
        ViewContainerRef } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Directive({
  selector: '[dynamicField]'
})
export class DynamicFieldDirective implements OnInit {
  @Input()
  config;

  @Input()
  group: FormGroup;
  
  constructor(
    private resolver: ComponentFactoryResolver,
    private container: ViewContainerRef
  ) {}
  
  ngOnInit() {
    
  }
}

上面代碼中摄欲,我們還添加了 ngOnInit 生命周期鉤子蝗拿。由于我們?cè)试S使用 inputselect 類型來聲明組件的類型,因此我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象來將字符串映射到相關(guān)的組件類蒿涎,具體如下:

// ...
import { FormButtonComponent } from '../form-button/form-button.component';
import { FormInputComponent } from '../form-input/form-input.component';
import { FormSelectComponent } from '../form-select/form-select.component';

const components = {
  button: FormButtonComponent,
  input: FormInputComponent,
  select: FormSelectComponent
};

@Directive(...)
export class DynamicFieldDirective implements OnInit {
  // ...
}

這將允許我們通過 components['button'] 獲取對(duì)應(yīng)的 FormButtonComponent 組件類哀托,然后我們可以把它傳遞給 ComponentFactoryResolver 對(duì)象以獲取對(duì)應(yīng)的 ComponentFactory (組件工廠):

// ...
const components = {
  button: FormButtonComponent,
  input: FormInputComponent,
  select: FormSelectComponent
};

@Directive(...)
export class DynamicFieldDirective implements OnInit {
  // ...
  ngOnInit() {
    const component = components[this.config.type];
    const factory = this.resolver.resolveComponentFactory<any>(component);
  }
  // ...
}

現(xiàn)在我們引用了配置中定義的給定類型的組件,并將其傳遞給 ComponentFactoryRsolver 對(duì)象提供的resolveComponentFactory() 方法劳秋。您可能已經(jīng)注意到我們?cè)?resolveComponentFactory 旁邊使用了 <any>仓手,這是因?yàn)槲覀円獎(jiǎng)?chuàng)建不同類型的組件。此外我們也可以定義一個(gè)接口玻淑,然后每個(gè)組件都去實(shí)現(xiàn)嗽冒,如果這樣的話 any 就可以替換成我們已定義的接口。

現(xiàn)在我們已經(jīng)有了組件工廠补履,我們可以簡(jiǎn)單地告訴我們的 ViewContainerRef 為我們創(chuàng)建這個(gè)組件:

@Directive(...)
export class DynamicFieldDirective implements OnInit {
  // ...
  component: any;
  
  ngOnInit() {
    const component = components[this.config.type];
    const factory = this.resolver.resolveComponentFactory<any>(component);
    this.component = this.container.createComponent(factory);
  }
  // ...
}

我們現(xiàn)在已經(jīng)可以將 configgroup 傳遞到我們動(dòng)態(tài)創(chuàng)建的組件中添坊。我們可以通過 this.component.instance 訪問到組件類的實(shí)例:

@Directive(...)
export class DynamicFieldDirective implements OnInit {
  // ...
  component;
  
  ngOnInit() {
    const component = components[this.config.type];
    const factory = this.resolver.resolveComponentFactory<any>(component);
    this.component = this.container.createComponent(factory);
    this.component.instance.config = this.config;
    this.component.instance.group = this.group;
  }
  // ...
}

接下來,讓我們?cè)?DynamicFormModule 中聲明已創(chuàng)建的 DynamicFieldDirective 指令:

// ...
import { DynamicFieldDirective } from './components/dynamic-field/dynamic-field.directive';

@NgModule({
  // ...
  declarations: [
    DynamicFieldDirective,
    DynamicFormComponent,
    FormButtonComponent,
    FormInputComponent,
    FormSelectComponent
  ],
  exports: [
    DynamicFormComponent
  ]
})
export class DynamicFormModule {}

如果我們直接在瀏覽器中運(yùn)行以上程序箫锤,控制臺(tái)會(huì)拋出異常贬蛙。當(dāng)我們想要通過 ComponentFactoryResolver 對(duì)象動(dòng)態(tài)創(chuàng)建組件的話,我們需要在 @NgModule 配置對(duì)象的一個(gè)屬性 - entryComponents 中谚攒,聲明需動(dòng)態(tài)加載的組件阳准。

@NgModule({
  // ...
  entryComponents: [
    FormButtonComponent,
    FormInputComponent,
    FormSelectComponent
  ]
})
export class DynamicFormModule {}

基本工作都已經(jīng)完成,現(xiàn)在我們需要做的就是更新 DynamicFormComponent 組件馏臭,應(yīng)用我們之前已經(jīng) DynamicFieldDirective 實(shí)現(xiàn)動(dòng)態(tài)組件的創(chuàng)建:

@Component({
  selector: 'dynamic-form',
  template: `
    <form
      class="dynamic-form"
      [formGroup]="form">
      <ng-container
        *ngFor="let field of config;"
        dynamicField
        [config]="field"
        [group]="form">
      </ng-container>
    </form>
  `
})
export class DynamicFormComponent implements OnInit {
  // ...
}

正如我們前面提到的野蝇,我們使用 <ng-container>作為容器來重復(fù)我們的動(dòng)態(tài)字段。當(dāng)我們的組件被渲染時(shí)括儒,這是不可見的绕沈,這意味著我們只會(huì)在 DOM 中看到我們的動(dòng)態(tài)創(chuàng)建的組件。

此外我們使用 *ngFor 結(jié)構(gòu)指令帮寻,根據(jù) config (數(shù)組配置項(xiàng)) 動(dòng)態(tài)創(chuàng)建組件乍狐,并設(shè)置 dynamicField 指令的兩個(gè)輸入屬性:config 和 group。最后我們需要做的是實(shí)現(xiàn)表單提交功能规婆。

表單提交

我們需要做的是為我們的 <form> 組件添加一個(gè) (ngSubmit) 事件的處理程序澜躺,并在我們的動(dòng)態(tài)表單組件中新增一個(gè) @Output 輸出屬性,以便我們可以通知使用它的組件抒蚜。

import { Component, Input, Output, OnInit, EventEmitter} from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'dynamic-form',
  template: `
    <form 
      [formGroup]="form"
      (ngSubmit)="submitted.emit(form.value)">
      <ng-container
       *ngFor="let field of config;"
       dynamicField
       [config]="field"
       [group]="form">
      </ng-container>
    </form>
  `
})
export class DynamicFormComponent implements OnInit {
  @Input() config: any[] = [];

  @Output() submitted: EventEmitter<any> = new EventEmitter<any>();
  // ...
}

最后我們同步更新一下 app.component.ts 文件:

import { Component } from '@angular/core';

@Component({
  selector: 'exe-app',
  template: `
    <div class="app">
      <dynamic-form 
        [config]="config"
        (submitted)="formSubmitted($event)">
      </dynamic-form>
    </div>
  `
})
export class AppComponent {
  // ...
  formSubmitted(value: any) {
    console.log(value);
  }
}

Toddmotto 大神線上示例 - angular-dynamic-forms掘鄙,查看完整代碼請(qǐng)?jiān)L問 - toddmott/angular-dynamic-forms

我有話說

在自定義表單控件組件中 [formGroup]="group" 是必須的么嗡髓?

form-input.component.ts

<div [formGroup]="group">
  <label>{{ config.label }}</label>
  <input
     type="text"
     [attr.placeholder]="config.placeholder"
     [formControlName]="config.name" />
</div>

如果去掉 <div> 元素上的 [formGroup]="group" 屬性操漠,重新編譯后瀏覽器控制臺(tái)將會(huì)拋出以下異常:

Error: formControlName must be used with a parent formGroup directive.  You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).
Example:

<div [formGroup]="myGroup">
  <input formControlName="firstName">
</div>

In your class:
this.myGroup = new FormGroup({
  firstName: new FormControl()
});

formControlName 指令中,初始化控件的時(shí)候饿这,會(huì)驗(yàn)證父級(jí)指令的類型:

private _checkParentType(): void {
    if (!(this._parent instanceof FormGroupName) &&
        this._parent instanceof AbstractFormGroupDirective) {
      ReactiveErrors.ngModelGroupException();
    } else if (
        !(this._parent instanceof FormGroupName) && 
        !(this._parent instanceof FormGroupDirective) &&
        !(this._parent instanceof FormArrayName)) {
      ReactiveErrors.controlParentException();
    }
  }

那為什么要驗(yàn)證浊伙,是因?yàn)橐研略龅目丶砑拥綄?duì)應(yīng) formDirective 對(duì)象中:

private _setUpControl() {
    this._checkParentType();
    this._control = this.formDirective.addControl(this);
    if (this.control.disabled && this.valueAccessor !.setDisabledState) {
      this.valueAccessor !.setDisabledState !(true);
    }
    this._added = true;
}

參考資源

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市长捧,隨后出現(xiàn)的幾起案子嚣鄙,更是在濱河造成了極大的恐慌,老刑警劉巖串结,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哑子,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡肌割,警方通過查閱死者的電腦和手機(jī)卧蜓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來把敞,“玉大人弥奸,你說我怎么就攤上這事》茉纾” “怎么了盛霎?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)耽装。 經(jīng)常有香客問我摩渺,道長(zhǎng),這世上最難降的妖魔是什么剂邮? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任摇幻,我火速辦了婚禮,結(jié)果婚禮上挥萌,老公的妹妹穿的比我還像新娘绰姻。我一直安慰自己,他們只是感情好引瀑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布狂芋。 她就那樣靜靜地躺著,像睡著了一般憨栽。 火紅的嫁衣襯著肌膚如雪帜矾。 梳的紋絲不亂的頭發(fā)上翼虫,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音屡萤,去河邊找鬼珍剑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛死陆,可吹牛的內(nèi)容都是我干的招拙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼措译,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼别凤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起领虹,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤规哪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后塌衰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體由缆,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年猾蒂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了均唉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肚菠,死狀恐怖舔箭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚊逢,我是刑警寧澤层扶,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站烙荷,受9級(jí)特大地震影響镜会,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜终抽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一戳表、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昼伴,春花似錦匾旭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至持舆,卻和暖如春色瘩,著一層夾襖步出監(jiān)牢的瞬間伪窖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工居兆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留覆山,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓史辙,卻偏偏與公主長(zhǎng)得像汹买,于是被迫代替她去往敵國(guó)和親佩伤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子聊倔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容