弄懂AngularJS select的数据绑定ngOptions

虽然项目已经进行一大半了,但是 ngOptions 还真的一直保留着困惑,其实第一眼看我怎么都感觉像是 Linq 一般,所以弄懂他才能够真正活用。而官方文档也只是把各种方式罗列一下,也没有示例,所以还是认认真真的学习一遍。

那么,我需要开始假设有以下数据,由于数组和对象数据源有些不同,以下是我假定的两种数据源。完整的示例

    $scope.users = [
        { name: 'cipchk', gender: '男', age: 25 },
        { name: '卡色', gender: '男', age: 18 },
        { name: '外卖从来没遇到美女', gender: '女', age: 18 },
        { name: '今晚不知道要吃什么', gender: '女', age: 18 },
        { name: '其实.NET很好', gender: '女', age: 18 },
        { name: 'fhw r', gender: '女', age: 18 }
    ];

    // 相当于c# Dictionary<int, string>
    $scope.product = {
        1: 'KFC',
        2: '麦当苏',
        3: '鱼丸',
        4: '星巴克',
        5: '错了,星巴克放在前面'
    };

一、label for value in arraylabel for (key , value) in object

<select ng-model="first.user" ng-options="item.name for item in users" class="form-control"></select>
<select ng-model="first.product" ng-options="value for (key, value) in product" class="form-control"></select>
  1. value等同于源码中的item(key, value),相当于c#中 foreach(var item in object) 一样,即每次获取一个元素放入item中。
  2. label等同于源码中的item.namevalue,相当于 <option>item.name</option> 一样。

执行结果:
1

二、 as 关键词

<select ng-model="first.user" ng-options="item.gender as item.name for item in users" class="form-control"></select>
<select ng-model="first.product" ng-options="key as value for (key, value) in product" class="form-control"></select>

直白点讲就是将 as 前面的变量赋于 ng-model

执行结果:
2

三、 group by 关键词

<select ng-model="third.user" ng-options="item.name group by item.gender for item in users" class="form-control"></select>
<select ng-model="third.product" ng-options="key as value group by key for (key, value) in product" class="form-control"></select>
  1. group by xx位置必须是 for 关键词之后。
  2. 分组实际是生成一个 <optgroup label="男"></optgroup> 标签。

四、完整表达式

所以的关键词已经全部说到了,这里我来弄一个完整的表达式,其意:对数据源users,以年龄分组,下拉列表文本是姓名、值为年龄。

<select ng-model="full.user" ng-options="item.age as item.name group by item.gender for item in users" class="form-control"></select>

其实把表达式拆分开来说,回过头看文章,好像并没有什么特别,也相当容易理解,只不过刚开始在结合文档时很晕。

附录:

  1. 在线示例代码
  2. 官网文档

以上。

AngularJS如何下载Excel文件

在AngularJS中要下载一个Excel文件到底有多难呢?

最简单的方法

这当然是放一个 a 链接元素在页面搞定。

<a href="/path/file.xlsx" target="_blank">下载文件</a>

可如果我们涉及到一些身份验证,而且又是通过 Cookie,浏览器会很怪的一并发送到服务端,是不是一切都很好呢?

如果……

像上面说的如果我需要自定义请求头,例如:OWIN等身份验证的情况下,怎么办呢?

问题

也许我们可以非常简单的通过 ajax 发送一个 get 请求,并填写相应 headers,比如:

$http.get({
    url: '/path/file.xlsx',
    method: 'get',
    headers: {
        Authorization: 'Bearer pTVhzRZgA6yW-fp8c5vcxzBxr6vuIBYQrlo0ASIVxgkfN6'
    }
}).success(function (data) {
    // 怎么保存?
});

有一个办法就是我们可以通过 HTML5a 元素,指定一段 Base64 数据编码,我们可以生成一个 a 链接,然后点击下载。

这种方式在我的实验中,发现对于 Excel 支持不好,对于大一点的文件,下载回来都是无法打开。

Blob

Blob 存储的是二进制,实则就是一个 JavaScript 下的一个 File 对象,目前被大部分流行浏览器所支持。

我这里还找到一个 FileServer.js 是对 Blob 保存的具体实现。

以下是我结合 FileServer.js 写的一个AngularJS指令,好了,废话不多说:

App.directive('downFile', ['$http',function ($http) {
    return {
        restrict: 'A',
        scope: true,
        link: function (scope, element, attr) {
            var ele = $(element);
            ele.on('click', function (e) {
                ele.prop('disabled', true);
                e.preventDefault();
                $http({
                    url: attr.downFile,
                    method: 'get',
                    responseType: 'arraybuffer'
                }).success(function (data, status, headers) {
                    ele.prop('disabled', false);
                    var type;
                    switch (attr.downFileType) {
                        case 'xlsx':
                            type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                            break;
                    }
                    if (!type) throw '无效类型';
                    saveAs(new Blob([data], { type: type }), decodeURI(headers()["x-filename"]));  // 中文乱码
                }).error(function (data, status) {
                    alert(data);
                    ele.prop('disabled', false);
                });
            });
        }
    };
}]);

相对于 View 的具体实现:

<button down-file="/order/export/{{item.id}}" down-file-type="xlsx" class="btn btn-green btn-sm">导出</button>

以下是 ASP.NET API 的具体实现:

HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new ByteArrayContent(pck.GetAsByteArray());
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.Add("x-filename", System.Web.HttpUtility.UrlEncode(item.title, System.Text.Encoding.UTF8) + ".xlsx"); // 中文乱码
return response;

中文文件名乱码问题

文件名为中文时获取到的 x-filename 会是乱码,所以需要进行编码,在示例中已经标识鸟。

写文章时比较仓促,所以示例中只对 Excel 进行转化,可以根据需求加入各种文件格式类型。

以上,希望帮助到各位。

AngularJS中ng-if和ng-show区别

ngIf

ngIf 根据表达式会移除或重新创建 DOM。

<div ng-if="!isEdit">列表</div>
<div ng-if="isEdit">编辑区</div>

以上假设 isEdit=false 时会移除编辑区DOM,反之会移除列表。正因为如此,这会导致带有位置性质的伪类(:first-child:last-child)无法被正常使用。

ngIf有着自己的 scope,虽然会继承自父 scope,但是当我们去更新数据 isEdit 值时,会重新创建一个新的 scope 并继承父 scope。

如果这里还有一个问题,当我们在某个 ngIf 下绑定某个 ‘ngModel’对象时,是不会直接同步更新到父 scope里。

ngShow

相对于 ngIf 并不会去重建 DOM,而只是单纯的设置 dispaly:none!important 的 style,所以可以最大的保证在同一个页面当中,不会倒置重建DOM引起的一些小小异常,比如数据丢失、伪类无法正常工作。

关于ASP.NET API的FromBody参数

越来越喜欢 asp.net web api,很大原因是因为非常干净。而其中 POST 是作为修改交互极为重要的一个请求。当然为了体验,和 asp.net web api交互总是离不开 jQuery 或者其实类似 ajax 请求方法,但往往会有一些小小诡异,比如:

咦,为什么都是null?

所以这篇文章会讲解一些 POST 请求时可能的一些问题。

POST原始参数数据到Asp.net web API

public void Post(string value){}

原则上我们只需要按标准的使用 $.post,比如:

$.post('/api/values', { value: 'asdf' });

可结果是:

POST http://localhost:53924/api/Values 404 (Not Found) 

WHY???很明显路由未找到,可这与我们期待不同呀。

必须标记参数[FromBody]

这是一种概念问题,因为对于 POST 时,按我们想当然自然是会自动将 POST 内容直接给 value。而对于路由而言这是一个完整的请求参数,自然就返回一个 404 错误。

我们需要给参数加上 [FromBody] 属性,比如:

public void Post([FromBody]string value){}
$.post('/api/values', { value: 'asdf'}, function(d) { console.log(d); });
// 输出:null

以上我们可以正常的请求成功。但输出的结果还是 null,这不应该我传递是 asdf,这一点我后面再提。

Asp.net web API 参数绑定规则

默认下,ASP.NET web API按以下两种规则绑定参数:

  1. 简单类型主要包括原始类型(int、bool、double等)含(TimeSpan、Guid待)以及String,默认会尝试从 URI 获取。
  2. 复杂类型会尝试从请求体获取,并通过媒体类型格局化器进行解析。

每一个方法只能有一个[FromBody]

除非你自定义一个模型绑定方法,否则你只能使用一个 [FromBody],而对于 POST大部分情况下是多个参数,所以最好的是采用复杂类型。

为什么只允许一个,这应该从认识 RESTful 架构开始,对于一个资源对应一个特定方法,而把数据分成几个松散参数也违背 ASP.NET API 吧。

public class Album {
    public string name {get;set;}
}
public void Post(int id, Album album){}
$.post('/api/values?id=1', { name: 'asdf'}, function(d) { console.log(d); });

以上,这是将简单类型和复杂类型同时使用。

[FromBody]参数必须进行键值编码

回过头看 null 问题,ASP.NET web API需要我们为参数指定 [FromBody],这是一种特定格式。正因为这种特定格式,会依赖 Content-Type类型来解析请求体应该采用什么格式,而绝大部分客户端和服务端采用的是键值对和JSON两种。

  1. 键值对:jQuery默认就是采用这种方式,而对于ASP.NET web API默认是空值做为取值条件,所以我们应该 $.post('/api/values', { '': 'asdf' }); 这样 value 参数才能够正常绑定。

  2. JSON:当Content-Type:application/json时,这也是大多数客户端所习惯采用的比如(AngularJS),对于这种情况我们应该 $.ajax( { type:'post', url: '/api/values', contentType: 'application/json', data: '"asdf"' }).done(function(d) { console.log(d); });

415 Unsupported Media Type

当我们传递的请求体内容无法被正常解析时,会得到这个错误。所以在传递请求体内容时要注意。

AngularJS ngClass用法

controller定义方法

    $scope.getMenuItemPropClasses = function(item) {
      return (item.heading ? 'nav-heading' : '') +
        (isActive(item) ? ' active' : '');
    };
// 使用:ng-class="getMenuItemPropClasses(item)"

对于复制逻辑而言这是唯一的方式,而大部分情况下只是很简单的判断,比较 bool 判断。

内嵌式

ng-class="{itemDisabled: (data.order_can_hit || 0) == 0}"

AngularJS 支持很非常的表达式,所以绝大部分,直接在views完成。

itemDisabled:为 class 名称,必须是符合 JavaScript 变量格式。

附录:
1.ngClass
2.Angular Expressions

© 2018 卡片机色彩 沪ICP备13032872号-3

Theme by cipchk

to top