Angular学习笔记(4)—内置指令

基础ng属性指令

布尔属性

布尔属性代表一个truefalse值。当这个属性出现时,这个属性的值就是true(无论实际定义的值是什么)。如果未出现,这个属性的值就是false
当在AngularJS中使用动态数据绑定时,不能简单地将这个属性值设置为turefalse,因为 根据标准定义只有当这个属性不出现时,它的值才为false。因此AngularJS提供了一组带有ng-前缀版本的布尔属性,通过运算表达式的值来决定在目标元素上是插入还是移除对应的属性。

1.ng-disabled

使用ng-disabled可以把disabled属性绑定到以下表单输入字段上:

<input> (text、checkbox、radio、number、url、email、submit);<textarea>;<select>;<button>。

当写普通的HTML输入字段时,如果在元素标签上出现了disabled属性就会禁用这个输入字段。通过ng-disabled可以对是否出现属性进行绑定。

<input type="text" ng-model="someProperty" placeholder="TypetoEnable">
<button ng-model="button" ng-disabled="!someProperty">AButton</button>

在下面的例子,文本字段会被禁用五秒,直到在$timeout中将isDisabled属性设置为true

<textarea ng-disabled="isDisabled">Wait5seconds</textarea>
angular.module('myApp', [])
       .run(function($rootScope, $timeout) {
           $rootScope.isDisabled = true;
           $timeout(function() {
               $rootScope.isDisabled = false;
           }, 5000);
       });
2.ng-readonly

通过ng-readonly可以将某个返回真或假的表达式同是否出现readonly属性进行绑定。

Type here to make sibling readonly:
<input type="text" ng-model="someProperty"><br/>
<input type="text" ng-readonly="someProperty" value="Some text here"/>
3.ng-checked

通过ng-checked将某个表达式同是否出现checked属性进行绑定。

//默认会把复选框勾选
<label>someProperty={{someProperty}}</label>
<input type="checkbox" ng-checked="someProperty" ng-init="someProperty=true" ng-model="someProperty">
4.ng-selected

ng-selected可以对是否出现option标签的selected属性进行绑定。

<label>Select Two Fish:</label>
<input type="checkbox" ng-model="isTwoFish"><br/>
<select>
    <option>One Fish</option>
    <option ng-selected="isTwoFish">Two Fish</option>
</select>

类布尔属性

ng-hrefng-src等属性虽然不是标准的HTML布尔属性,但是由于行为相似,所以在AngularJS源码内部是和布尔属性同等对待的。

1.ng-href

当使用当前作用域中的属性动态创建URL时,应该用ng-href代替href
这里的潜在问题是当用户点击一个由插值动态生成的链接时,如果插值尚未生效,将会跳转到错误的页面(通常是404)。这时,如果使用ng-href,AngularJS会等到插值生效后再执行点击链接的行为。

<!-- 当href包含一个{{expression}}时总是使用ng-href-->
<a ng-href="{{ myHref }}">I'm feeling lucky, when I load</a>
<!-- 用户单击之前,href不会加载 -->
<a href="{{myHref}}">I'm feeling 404</a>

将插值生效的事件延迟两秒,来观察实际的行为。

angular.module('myApp',[]).run(function($rootScope,$timeout) {
    $timeout(function() {
        $rootScope.myHref = 'http://google.com';
    }, 2000);
});
2.ng-src

AngularJS会告诉浏览器在ng-src对应的表达式生效之前不要加载图像。

<h1>WrongWay</h1>
<img src="{{imgSrc}}"/>
<h1>Rightway</h1>
<img ng-src="{{imgSrc}}"/>

angular.module('myApp',[]).run(function($rootScope,$timeout) {
    $timeout(function() {
        $rootScope.imgSrc='https://www.google.com/images/srpr/logo11w.png';
    }, 2000);
});

在指令中使用子作用域

ng-appng-controller是特殊的指令,因为它们会修改嵌套在它们内部的指令的作用域。
ng-app为AngularJS应用创建$rootScopeng-controller则会以$rootScope或另外一个ng-controller的作用域为原型创建新的子作用域。

1.ng-app

任何具有ng-app属性的DOM元素将被标记为$rootScope的起始点。$rootScope是作用域链的起始点,任何嵌套在ng-app内的指令都会继承它。在JS代码中通过run方法来访问$rootScope

<html ng-app="myApp">
    <body>
        {{ someProperty }}
        <button ng-click="someAction()"></button>
    </body>
</html>

angular.module('myApp',[]).run(function($rootScope) {
    $rootScope.someProperty='hello computer';
    $rootScope.someAction=function() {
        $rootScope.someProperty='hello human';
    };
});

2.ng-controller

内置指令ng-controller的作用是为嵌套在其中的指令创建一个子作用域,避免将所有操作和模型都定义在$rootScope上。用这个指令可以在一个DOM元素上放置控制器。
ng-controller接受一个参数expression,这个参数是必需的。expression参数是一个AngularJS表达式。
$scope只是一个JS对象,其中含有从父级$scope中通过原型继承得到的方法和属性,包括应用的$rootScope。嵌套在ng-controller中的指令有访问新子$scope的权限。
$scope对象的职责是承载DOM中指令所共享的操作和模型。操作指的是$scope上的标准JS方法。模型指的是$scope上保存的包含瞬时状态数据的JS对象。持久化状态的数据应该保存到服务中,服务的作用是处理模型的持久化。

<div ng-controller="SomeController">
    {{ someModel.someProperty }}
    <button ng-click="someAction()">Communicate</button>
</div>
angular.module('myApp',[]).controller('SomeController',function($scope) {
    //创建模型
    $scope.someModel = {
        // 添加属性
        someProperty: 'hello computer'
    }
    // 设置$scope自身的操作
    $scope.someAction = function() {
        $scope.someModel.someProperty = 'hello human';
    };
});

在这个例子中,首先,我们使用了$rootScope的子作用域,它提供了一个干净的对象供我们操作。使用子作用域意味着其上的数据模型和操作在应用的其他地方是无法访问的,只能被这个作用域内的指令及其子作用域访问。其次,显式声明了数据模型。

<div ng-controller="SomeController">
    {{ someBareValue }}
    <button ng-click="someAction()">Communicate to child</button>
    <div ng-controller="ChildController">
        {{ someBareValue }}
        <button ng-click="childAction()">Communicate to parent</button>
    </div>
</div>
angular.module('myApp',[]).controller('SomeController',function($scope) {
    // 反模式,裸值
    $scope.someBareValue = 'hello computer';
    // 设置 $scope 本身的操作,这样没问题
    $scope.someAction = function() {
        //在SomeController和ChildController中设置{{someBareValue}}
        $scope.someBareValue = 'hello human, from parent';
    };
})
.controller('ChildController', function($scope) {
    $scope.childAction = function() {
    // 在ChildController中设置{{ someBareValue }}
        $scope.someBareValue = 'hello human, from child';
    };
});

这个例子中,在已有的控制器中嵌套了第二个控制器,并且没有设置模型对象的属性。由于原型继承的关系,修改父级对象中的someBareValue会同时修改子对象中的值,但反之则不行。这个例子充分说明了子控制器是复制而非引用someBareValue
如果将模型对象的某个属性设置为字符串,它会通过引用进行共享,因此在子$scope中修改属性也会修改父$scope中的这个属性。下面的例子展示了正确的做法:

<div ng-controller="SomeController">
    {{ someModel.someValue }}
    <button ng-click="someAction()">Communicate to child</button>
    <div ng-controller="ChildController">
        {{ someModel.someValue }}
        <button ng-click="childAction()">Communicate to parent</button>
    </div>
</div>
angular.module('myApp', [])
       .controller('SomeController', function($scope) {
           // 最佳实践,永远使用一个模式
           $scope.someModel = {
               someValue: 'hello computer'
           }
           $scope.someAction = function() {
               $scope.someModel.someValue = 'hello human, from parent';
           };
       })
       .controller('ChildController', function($scope) {
           $scope.childAction = function() {
               $scope.someModel.someValue = 'hello human, from child';
           };
       });

无论点击哪个按钮,值都会进行同步修改。
注意,虽然这个特性是使用ng-controller时最重要的特性之一,但在使用任何会创建子作用域的指令时,如果将指令定义中的scope设置为true,这个特性也会带来负面影响。下面的内置指令都有同样的特性:ng-includeng-switchng-repeatng-viewng-controllerng-if

3.ng-include

使用ng-include可以加载、编译并包含外部HTML片段到当前的应用中。模板的URL被限制在与应用文档相同的域和协议下,可以通过白名单或包装成被信任的值来突破限制。更进一步,需要考虑跨域资源共享(CORS)和同源规则来确保模板可以在任何浏览器中正常加载。
使用ng-include时AngularJS会自动创建一个子作用域。如果你想使用某个特定的作用域,例如ControllerA的作用域,必须在同一个DOM元素上添加ng-controller="ControllerA"指令,这样当模板加载完成后,不会像往常一样从外部作用域继承并创建一个新的子作用域。

<div ng-include="other.html" ng-controller="MyController" ng-init="name='World'">
    Hello {{name}}
</div>
<!--ng-include等价写法-->
<div ng-include src="other.html">
    Hello {{name}}
</div>

4.ng-switch

这个指令和ng-switch-whenon="propertyName"一起使用,可以在propertyName发生变化时渲染不同指令到视图中。

<input type="text" ng-model="person.name"/>
<div ng-switch on="person.name">
    <p ng-switch-default>And the winner is</p>
    <h1 ng-switch-when="Ari">{{person.name}}</h1>
</div>

switch被调用之前用ng-switch-default来输出默认值。

5.ng-view

ng-view指令用来设置将被路由管理和放置在HTML中的视图的位置。

6.ng-if

使用ng-if指令可以完全根据表达式的值在DOM中生成或移除一个元素。如果赋值给ng-if的表达式的值是false,那对应的元素将会从DOM中移除,否则对应元素的一个克隆将被重新插入DOM中。
ng-ifng-showng-hide指令最本质的区别是,它不是通过CSS显示或隐藏DOM节点,而是真正生成或移除节点。当一个元素被ng-if从DOM中移除,同它关联的作用域也会被销毁。而且当它重新加入DOM中时,会通过原型继承从它的父作用域生成一个新的作用域。
ngIf重新创建元素时用的是它们编译后的状态。如果ng-if内部的代码加载之后被jQuery修改过(例如用.addClass),那么当ng-if的表达式值为false时,这个DOM元素会被移除,表达式再次成为true时这个元素及其内部的子元素会被重新插入DOM,此时这些元素的状态会是它们的原始状态,而不是它们上次被移除时的状态。也就是说无论用jQuery的.addClass添加了什么类都不会存在了。

<div ng-if="2+2===4">
    Hi, I do exist
</div>

7.ng-repeat

ng-repeat用来遍历一个集合或为集合中的每个元素生成一个模板实例。集合中的每个元素都会被赋予自己的模板和作用域。同时每个模板实例的作用域中都会暴露一些特殊的属性。

  • $index:遍历的进度(0...length-1
  • $first:当元素是遍历的第一个时值为true
  • $middle:当元素处于第一个和最后元素之间时值为true
  • $last:当元素是遍历的最后一个时值为true
  • $even:当$index值是偶数时值为true
  • $odd:当$index值是奇数时值为true
<ul ng-controller="PeopleController">
    <li ng-repeat="person in people" ng-class="{even:!$even, odd:!$odd}">
        {{person.name}} lives in {{person.city}}
    </li>
</ul>

.odd { background-color: blue;}
.even { background-color: red;}

angular.module('myApp',[]).controller('PeopleController',function($scope) {
    $scope.people = [
        {name: "Ari", city: "Beijing"},
        {name: "Erik", city: "Seattle"}
    ];
});

8.ng-init

ng-init指令用来在指令被调用时设置内部作用域的初始状态。

<div ng-init="greeting='Hello';person='World'">
    {{greeting}} {{person}}
</div>

9. {{ }}

{{ }}语法是AngularJS内置的模板语法,它会在内部$scope和视图之间创建绑定。基于这个绑定,只要$scope发生变化,视图就会随之自动更新。
事实上它也是指令,是ng-bind的简略形式,用这种形式不需要创建新的元素,因此它常被用在行内文本中。在屏幕可视的区域内使用{{ }}会导致页面加载时未渲染的元素发生闪烁,用ng-bind可以避免这个问题。

<body ng-init="greeting='HelloWorld'">
    {{greeting}}
</body>

10.ng-bind

尽管可以在视图中使用{{ }}模板语法,我们也可以通过ng-bind指令实现同样的行为。

<body ng-init="greeting='HelloWorld'">
    <p ng-bind="greeting"></p>
</body>

HTML加载含有{{ }}语法的元素后并不会立刻渲染它们,导致未渲染内容闪烁。我可以用ng-bind将内容同元素绑定在一起避免未渲染内容闪烁。内容会被当作子文本节点渲染到含有ng-bind指令的元素内。

11.ng-cloak

除使用ng-bind来避免未渲染元素闪烁,还可以在含有{{ }}的元素上使用ng-cloak指令。

<body ng-init="greeting='HelloWorld'">
    <p ng-cloak>{{ greeting }}</p>
</body>

ng-cloak指令会将内部元素隐藏,直到路由调用对应的页面时才显示出来。

12.ng-bind-template

ng-bind指令类似,ng-bind-template用来在视图中绑定多个表达式。

<div ng-bind-template="{{message}}{{name}}"></div>

13.ng-model

ng-model指令用来将input、select、text area或自定义表单控件同包含它们的作用域中的属性进行绑定。它可以提供并处理表单验证功能,在元素上设置相关的CSS类(ng-valid、ng-invalid等),并负责在父表单中注册控件。
它将当前作用域中运算表达式的值同给定的元素进行绑定。如果属性并不存在,它会隐式创建并将其添加到当前作用域中。
我们应该始终用ngModel来绑定$scope上一个数据模型内的属性,而不是$scope上的属性,这可以避免在作用域或后代作用域中发生属性覆盖。

<input type="text" ng-model="modelName.someProperty" />

14.ng-show和ng-hide

ng-showng-hide根据所给表达式的值来显示或隐藏HTML元素。当赋值给ng-show指令的值为false时元素会被隐藏。类似地,当赋值给ng-hide指令的值为true时元素也会被隐藏。
元素的显示或隐藏是通过移除或添加ng-hide这个CSS类来实现的。.ng-hide类被预先定义在了AngularJS的CSS文件中,并且它的display属性的值为none(用了!important标记)。

<div ng-show="2 + 2 == 5">
    2 + 2 isn't 5, don't show
</div>
<div ng-hide="2 + 2 == 4">
    2 + 2 is 4, do hide
</div>

15.ng-change

这个指令会在表单输入发生变化时计算给定表达式的值。因为要处理表单输入,这个指令要和ngModel联合起来使用。

<div ng-controller="EquationController">
    <input type="text" ng-model="equation.x" ng-change="change()" />
    <code>{{ equation.output }}</code>
</div>
angular.module('myApp',[])
       .controller('EquationController',function($scope) {
           $scope.equation = {};
           $scope.change = function() {
               $scope.equation.output= parseInt($scope.equation.x) + 2;
           };
       });

上面的例子中,只要文本输入字段中的内容发生了变化就会改变equation.x的值,进而运行change()函数。

16.ng-form

ng-form用来在一个表单内部嵌套另一个表单。普通的HTML <form>标签不允许嵌套,但ng-form可以。这意味着内部所有的子表单都合法时,外部的表单才会合法。这对于用ng-repeat动态创建表单是非常有用的。
由于不能通过字符插值来给输入元素动态地生成name属性,所以需要将ng-form指令内每组重复的输入字段都包含在一个外部表单元素内。
下面的CSS类会根据表单的验证状态自动设置:

  • 表单合法时设置ng-valid
  • 表单不合法时设置ng-invlid
  • 表单未进行修改时设置ng-pristion
  • 表单进行过修改时设置ng-dirty

AngularJS不会将表单提交到服务器,除非它指定了action属性。要指定提交表单时调用哪个JS方法,使用下面两个指令中的一个。

  • ng-submit:在表单元素上使用。
  • ng-click:在第一个按钮或submit类型的输入字段上使用。

为了避免处理程序被多次调用,只使用两个指令中的一个。
下面的例子展示了如何通过服务器返回的JSON数据动态生成一个表单。我们用ng-loop来遍历从服务器取回的所有数据。由于不能动态生成name属性,而我们又需要这个属性做验证,所以在循环的过程中会为每一个字段都生成一个新表单。
由于AngularJS中用来取代<form>ng-form指令可以嵌套,并且外部表单在所有子表单都合法之前一直处于不合法状态,因此我们可以在动态生成子表单的同时使用表单验证功能。

angular.module('myApp',[])
    .controller('FormController',function($scope) {
        $scope.fields = [
            {placeholder: 'Username', isRequired: true},
            {placeholder: 'Password', isRequired: true},
            {placeholder: 'Email (optional)', isRequired: false}
        ];
        $scope.submitForm = function() {
            alert("it works!");
        };
    });

下面用这些数据生成一个有验证功能的动态表单:

<form name="signup_form" ng-controller="FormController" ng-submit="submitForm()" novalidate>
    <div ng-repeat="field in fields" ng-form="signup_form_input">
        <input type="text" name="dynamic_input" ng-required="field.isRequired" ng-model="field.name" placeholder="{{field.placeholder}}" />
        <div ng-show="signup_form_input.dynamic_input.$dirty && signup_form_input.dynamic_input.$invalid">
            <span class="error" ng-show="signup_form_input.dynamic_input.$error.required">
                The field is required.
            </span>
        </div>
    </div>
    <button type="submit" ng-disabled="signup_form.$invalid">Submit All</button>
</form>

input.ng-invalid {border: 1px solid red;}
input.ng-valid {border: 1px solid green;}

17.ng-click

ng-click用来指定一个元素被点击时调用的方法或表达式。

<div ng-controller="CounterController">
    <button ng-click="count=count+1" ng-init="count=0">Increment</button>
    count: {{ count }}
    <button ng-click="decrement()">Decrement</button>
<div>

angular.module('myApp',[]).controller('CounterController',function($scope) {
    $scope.decrement = function() {
        $scope.count = $scope.count-1;
    };
})

18.ng-select

ng-select用来将数据同HTML的<select>元素进行绑定。这个指令可以和ng-model以及ng-options指令一同使用。
ng-options可以接受一个数组或对象,并对它们进行循环,将内部的内容提供给select标签内部的选项。它可以是下面两种形式。
数组作为数据源:

  • 用数组中的值做标签;
  • 用数组中的值作为选中的标签;
  • 用数组中的值做标签组;
  • 用数组中的值作为选中的标签组。

对象作为数据源:

  • 用对象的键值(key-value)做标签;
  • 用对象的键值作为选中的标签;
  • 用对象的键值作为标签组;
  • 用对象的键值作为选中的标签组。
<div ng-controller="CityController">
    <select ng-model="city" ng-options="city.name for city in cities">
        <option value="">Choose City</option>
    </select>
    Best City: {{ city.name }}
</div>
angular.module('myApp',[]).controller('CityController',function($scope) {
    $scope.cities = [
        {name: 'Seattle'},
        {name: 'San Francisco'},
        {name: 'Chicago'},
        {name: 'New York'}
    ];
});

19.ng-submit

ng-submit用来将表达式同onsubmit事件进行绑定。这个指令同时会阻止默认行为(发送请求并重新加载页面),除非表单不含有action属性。

<form ng-submit="submit()" ng-controller="FormController">
    Enter text and hit enter:
    <input type="text" ng-model="person.name" name="person.name" />
    <input type="submit" name="person.name" value="Submit" />
    <code>people={{people}}</code>
    <ul ng-repeat="(index, object) in people">
        <li>{{ object.name }}</li>
    </ul>
</form>

angular.module('myApp',[]).controller('FormController',function($scope) {
    $scope.person={ name:null};
    $scope.people=[];
    $scope.submit=function() {
        if ($scope.person.name) {
            $scope.people.push({name: $scope.person.name});
            $scope.person.name = '';
        }
    };
});

20.ng-class

使用ng-class动态设置元素的类,方法是绑定一个代表所有需要添加的类的表达式。重复的类不会添加。当表达式发生变化,先前添加的类会被移除,新类会被添加。

<div ng-controller="LotteryController">
    <div ng-class="{red:x>5}" ng-if="x>5">You won!</div>
    <button ng-click="x=generateNumber()" ng-init="x=0">Draw Number</button>
    <p>Number is: {{ x }}</p>
</div>

.red {background-color: red;}

angular.module('myApp',[]).controller('LotteryController',function($scope) {
    $scope.generateNumber = function() {
        return Math.floor((Math.random()*10)+1);
    };
});

21.ng-attr-(suffix)

当AngularJS编译DOM时会查找花括号{{some expression}}内的表达式。这些表达式会被自动注册到$watch服务中并更新到$digest循环中,成为它的一部分。

<-- updated when`someExpression` on the$scope is updated -->
<h1>Hello{{someExpression}}</h1>

有时浏览器会对属性进行很苛刻的限制。SVG就是一个例子:

<svg>
    <circle cx="{{cx}}"></circle>
</svg>

运行上面的代码会抛出一个错误,指出我们有一个非法属性。可以用ng-attr-cx来解决这个问题。注意,cx位于这个名称的尾部。在这个属性中,通过用{{ }}来写表达式,达到前面提到的目的。

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

推荐阅读更多精彩内容