博客 (119)

在学习和使用 ASP.NET Web API 之前,最好先对 RESTful 有所了解。它是一种软件架构风格、设计风格,而不是标准。


推荐视频教程:https://www.imooc.com/learn/811

讲师:会飞的鱼Xia

需时:2小时25分


REST API 测试工具:

在 Chrome 网上应用店中搜索:Restlet Client

或网站 https://client.restlet.com


>>> 资源路径

SCHEME :// HOST [ ":" PORT ] [ PATH [ "?" QUERY ]]

其中 PATH 中的资源名称应该使用复数名词,举例:

GET https://xoyozo.net/api/Articles/{id}

POST https://xoyozo.net/api/Articles


>>> HTTP verb(对应 CURD 操作):

方法功能ASP.NET Web API 接口返回类型(一般的)
GET取一个或多个资源T 或 IEnumerable<T>
POST增加一个资源T
PUT修改一个资源T
DELETE删除一个资源void
PATCH更新一个资源(资源的部分属性)
HEAD获取报头
OPTIONS查询服务器性能或资源相关选项和需求


>>> 过滤信息:

如分页、搜索等


>>> 常用状态码:

200
OK
指示请求已成功
201
Created
资源创建成功(常见用例是一个 PUT 请求的结果)
202
Accepted
该请求已被接收但尚未起作用。它是非承诺的,这意味着HTTP中没有办法稍后发送指示处理请求结果的异步响应。
204
No Content
成功 PUT(修改)或 DELETE(删除)一个资源后返回空内容(ASP.NET Web API 中将接口返回类型设置为 void 即可)
400
Bad Request
服务器无法理解请求(如必填项未填、内容超长、分页大小超限等),幂等
401
Unauthorized
未授权(如用户未登录时执行了须要登录才能执行的操作),通俗地讲,是服务器告诉你,“你没有通过身份验证 - 根本没有进行身份验证或验证不正确 - 但请重新进行身份验证并再试一次。”这是暂时的,服务器要求你在获得授权后再试一次。
403
Forbidden
服务器理解请求但拒绝授权(是永久禁止的并且与应用程序逻辑相关联(如不正确的密码、尝试删除其它用户发表的文章)、IP 被禁止等),通俗地讲,是服务器告诉你,“我很抱歉。我知道你是谁 - 我相信你说你是谁 - 但你只是没有权限访问此资源。”
404
Not Found
找不到请求的资源(指资源不存在,如果找到但无权访问则应返回 403)
405
Method Not Allowed
请求方法被禁用(HTTP 动词)
500
Internal Server Error
服务器遇到阻止它履行请求的意外情况(如数据库连接失败)
503
Service Unavailable
服务器尚未准备好处理请求(常见原因是服务器因维护或重载而停机,这是临时的,可用于在单线程处理事务时遇到被锁定时的响应,如抽奖活动、抢楼活动,防止因并发导致逻辑错误)


>> 错误处理:

C# 例:throw new HttpResponseException(HttpStatusCode.NotFound);

PHP 例:throw new Exception('文章不存在', 404);


>>> 返回结果:

JSON/XML

不要返回密码等机密字段


>>> 其它:

身份验证窗口(浏览器弹窗,类似 alert,非页面表单),明文传输,安全性不高,不建议使用。实现:

在 Headers 中添加 Authorization:Basic “用户名:密码”的 Base64 编码


xoyozo 8 年前
5,840

本文不定时更新中……

收集了一些在开发过程中遇到的一些问题的解决方法,适合新手。


异常:

出现脚本错误或者未正确调用 Page()

原因:不小心删了第一行内容:<template>


异常:

模块编译失败:TypeError: Cannot read property 'for' of undefined

at fixDefaultIterator (D:\HBuilderX\plugins\uniapp\lib\mpvue-template-compiler\build.js:4277:24)

at mark (D:\HBuilderX\plugins\uniapp\lib\mpvue-template-compiler\build.js:4306:5)

at markComponent (D:\HBuilderX\plugins\uniapp\lib\mpvue-template-compiler\build.js:4371:5)

at baseCompile (D:\HBuilderX\plugins\uniapp\lib\mpvue-template-compiler\build.js:4384:15)

at compile (D:\HBuilderX\plugins\uniapp\lib\mpvue-template-compiler\build.js:4089:28)

at Object.module.exports (D:\HBuilderX\plugins\uniapp\lib\mpvue-loader\lib\template-compiler\index.js:43:18)

原因:新建的页面(简单模板)只有以下 3 个标签,须在 <template /> 中添加一些代码,如 <view />

<template>
</template>

<script>
</script>

<style>
</style>

异常:

模块编译失败:TypeError: Cannot read property 'toString' of undefined

at Object.preprocess (D:\HBuilderX\plugins\uniapp\lib\preprocess\lib\preprocess.js:56:15)

at Object.module.exports (D:\HBuilderX\plugins\uniapp\lib\preprocessor-loader.js:9:25)

原因:没有原因,纯抽风,HX 关掉再开就好了。


异常:

Cannot set property 'xxx' of undefined;at pages/... onLoad function;at api request success callback function

原因:属性未定义,例如 

data() {
	return {
		item: { }
	}
}

而直接赋值 this.item.abc.xxx = '123';

解决:

data() {
	return {
		item: {
			abc: ''
		}
	}
}

问:page 页面怎样修改 tabBar?

答:官方文档未给出答案,百度了一圈也无果(2018-10-23),但有人说小程序的 setTabBarBadge() 方法设置角标是可以用的。


坑:

VM1694:1 获取 wx.getUserInfo 接口后续将不再出现授权弹窗,请注意升级

参考文档: https://developers.weixin.qq.com/community/develop/doc/0000a26e1aca6012e896a517556c01

填坑:放弃使用 uni.getUserInfo 接口来获取用户信息,uni.login 可返回用于换取 openid / unionid 的 code,参:uni.login、 code2Session


坑:字符搜索(当前目录)(Ctrl+Alt+F)搜不出所有结果

填坑:顾名思义他只搜索当前目录,即当前打开文件所在目录,而非我误认为的整个项目根目录。在“项目管理器”中选中要搜索字符的目录即可。


坑:uni.navigateTo() 或 uni.redirectTo() 没反应

填坑:这两个方法不允许跳转到 tabbar 页面,用 uni.switchTab() 代替。


坑:使用“Ctrl+/”快捷键弹出“QQ五笔小字典”窗口

解决:打开QQ五笔“属性设置”,切换到“快捷键设置”选项卡,把“五笔小字典”前的勾取消(即使该组合键是设置为Ctrl+?)。


坑:<rich-text /> 中的 <img /> 太大,超出屏幕宽度

填坑:用正则表达式给 <img /> 加上最大宽度

data.data.Content = data.data.Content.replace(/\<img/gi, '<img style="max-width:100%;height:auto" ');

坑:无法重命名或删除目录或文件

填坑一:“以管理员身份运行”HBuilder X 后再试。

填坑二:关闭微信开发者工具、各种手机和模拟器后再试。

填坑三:打开“任务管理器”,结束所有“node.exe”进程后再试。


坑:

 thirdScriptError 
 sdk uncaught third Error 
 (intermediate value).$mount is not a function 
 TypeError: (intermediate value).$mount is not a function
Page[pages/xxxx/xxxx] not found. May be caused by: 1. Forgot to add page route in app.json. 2. Invoking Page() in async task.
Page is not constructed because it is not found.

填坑:关闭微信开发者工具、各种手机和模拟器后,删除“unpackage”目录。


坑:

Unexpected end of JSON input;at "pages/news/view" page lifeCycleMethod onLoad function
SyntaxError: Unexpected end of JSON input

填坑:给 uni.navigateTo() 的 url 传参时,如果简单地将对象序列化 JSON.stringify(item),那么如果内容中包含“=”等 url 特殊字符,就会发生在接收页面 onLoad() 中无法获取到完整的 json 对象,发生异常。

uni.navigateTo({
	url: "../news/view?item=" + JSON.stringify(item)
})

所以应该把参数值编码:

uni.navigateTo({
	url: "../news/view?item=" + escape(JSON.stringify(item))
})

如果是一般的 web 服务器来接收,那么会自动对参数进行解码,但 uni-app 不会,如果直接使用:

onLoad(e) {
	this.item = JSON.parse(e.item);
}

会发生异常:

Unexpected token % in JSON at position 0;at "pages/news/view" page lifeCycleMethod onLoad function
SyntaxError: Unexpected token % in JSON at position 0

需要解码一次:

onLoad(e) {
	this.item = JSON.parse(unescape(e.item));
}

需要注意的是,unescape(undefined) 会变成 'undefined',如果要判断是否 undefined,应是 unescape 之前。


坑:图片变形

填坑:mode="widthFix"


坑:页面如何向 tabBar 传参

填坑:全局或缓存


坑:编译为 H5 后,出现:Access-Control-Allow-Origin

填坑:参阅


坑:编译为 H5 后,GET 请求的 URL 中出现“?&”

填坑 :客户端只求 DCloud 官方能够尽快修复这个 bug,IIS 端可以暂时用 URL 重写来防止报 400 错误,参此文


坑:[system] errorHandler TypeError: Cannot read property 'forEach' of undefined

填坑:待填


xoyozo 8 年前
24,067

本文不定时更新中……


具备 RESTful 相关知识会更有利于学习 ASP.NET Web API:RESTful API 学习笔记

ASP.NET Web API 官网:https://www.asp.net/web-api


服务 URI Pattern

ActionHttp verbURI说明
Get contact listGET/api/contacts列表
Get filtered contactsGET/api/contacts?$top=2筛选列表
Get contact by IDGET/api/contacts/id获取一项
Create new contactPOST/api/contacts增加一项
Update a contactPUT/api/contacts/id修改一项
Delete a contactDELETE/api/contacts/id
删除一项


返回类型Web API 创建响应的方式
void返回空 204 (无内容)
HttpResponseMessage转换为直接 HTTP 响应消息。
IHttpActionResult调用 ExecuteAsync 来创建 HttpResponseMessage,然后将转换为 HTTP 响应消息。
其他类型将序列化的返回值写入到响应正文中;返回 200 (正常)。

HttpResponseMessage

可提供大量控制的响应消息。 例如,以下控制器操作设置的缓存控制标头。

public class ValuesController : ApiController
{
    public HttpResponseMessage Get()
    {
        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
        response.Content = new StringContent("hello", Encoding.Unicode);
        response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromMinutes(20)
        };
        return response;
    } 
}

IHttpActionResult

public IHttpActionResult Get (int id)
{
    Product product = _repository.Get (id);
    if (product == null)
    {
        return NotFound(); // Returns a NotFoundResult
    }
    return Ok(product);  // Returns an OkNegotiatedContentResult
}

其他的返回类型

响应状态代码为 200 (正常)。此方法的缺点是,不能直接返回错误代码,如 404。 但是,您可以触发 HttpResponseException 的错误代码


完善 Help 页

一般都是通过元数据注释(Metadata Annotations)来实现的描述的。


显示接口和属性的描述:

打开 HelpPage 区域的 App_Start 目录下的 HelpPageConfig.cs 文件,在 Register 方法的首行取消注释行:

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

在项目右键属性“生成”页,勾选“XML 文档文件”,并填写与上述一致的路径(App_Data/XmlDocument.xml)。


给接口加上 ResponseType 显示响应描述和示例:[ResponseType(typeof(TEntity))]


在实体模型的属性上加入 RequiredAttribute 特性可提供请求或响应中的实体的属性描述,如:[Required]


推荐使用:Swashbuckle

关于格式


在 WebApiConfig.Register() 中加入以下代码以禁止以 XML 格式输出(请按需设置):

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

Json 序列化去掉 k__BackingField

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver { IgnoreSerializableAttribute = true };

如果属性值为 null 则不序列化该属性

config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };



xoyozo 8 年前
6,508

image.png

你可能打开了假的工具[捂嘴]

查看你打开工具的图标快捷方式是指向到哪的?

image.png

打开属性,点击“打开文件所在的位置”,是不是那个英文名的文件是高亮的?

你可以直接双击打开这个英文工具“wechatdevtools.exe”,确实打开的是上面的 NW.JS 界面,

然后打开中文名的工具“微信web开发者工具.exe”,正常运行。

那么就在这个文件上右键,选择你喜欢的打开方式吧:“固定到开始屏幕”或“固定到任务栏”或“发送到桌面快捷方式”。

附微信web开发者工具下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

xoyozo 8 年前
12,867

制作类似下图中的拖拽排序功能:

image.png

1. 首先数据库该表中添加字段 sort,类型为 double(MySQL 中为 double(0, 0))。

2. 页面输出绑定数据(以 ASP.NET MVC 控制器为例):

public ActionResult EditSort()
{
    if (!zConsole.Logined) { return RedirectToAction("", "SignIn", new { redirect = Request.Url.OriginalString }); }

    db_auto2018Entities db = new db_auto2018Entities();

    return View(db.dt_dealer.Where(c => c.enabled).OrderBy(c => c.sort));
}

这里可以加条件列出,即示例中 enabled == true 的数据。

3. 前台页面引用 jQuery 和 jQuery UI。

4. 使用 <ul /> 列出数据:

<ul id="sortable" class="list-group gutter list-group-lg list-group-sp">
    @foreach (var d in Model)
    {
        <li class="list-group-item" draggable="true" data-id="@d.id">
            <span class="pull-left"><i class="fa fa-sort text-muted fa m-r-sm"></i> </span>
            <div class="clear">
                【id=@d.id】@d.name_full
            </div>
        </li>
    }
</ul>

5. 初始化 sortable,当拖拽结束时保存次序:

<script>
    var url_SaveSort = '@Url.Action("SaveSort")';
</script>
<script>
    $("#sortable").sortable({
        stop: function (event, ui) {
            // console.log('index: ' + $(ui.item).index())
            // console.log('id: ' + $(ui.item).data('id'))
            // console.log('prev_id: ' + $(ui.item).prev().data('id'))
            $.post(url_SaveSort, {
                id: $(ui.item).data('id'),
                prev_id: $(ui.item).prev().data('id')
            }, function (json) {
                if (json.result.success) {
                    // window.location.reload();
                } else {
                    toastr["error"](json.result.info);
                }
            }, 'json');
        }
    });
    $("#sortable").disableSelection();
</script>

这里回传到服务端的参数为:当前项的 id 值、拖拽后其前面一项的 prev_id 值(若移至首项则 prev_id 为 undefined)。

不使用 $(ui.item).index() 是因为,在有筛选条件的结果集中排序时,使用该索引值配合 LINQ 的 .Skip 会引起取值错误。

6. 控制器接收并保存至数据库:

[HttpPost]
public ActionResult SaveSort(int id, int? prev_id)
{
    if (!zConsole.Logined)
    {
        return Json(new { result = new { success = false, msg = "请登录后重试!" } }, JsonRequestBehavior.AllowGet);
    }

    db_auto2018Entities db = new db_auto2018Entities();

    dt_dealer d = db.dt_dealer.Find(id);

    // 拖拽后其前项 sort 值(若无则 null)(此处不需要加 enabled 等筛选条件)
    double? prev_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.id == prev_id).Select(c => c.sort).Single()
        : null as double?;

    // 拖拽后其后项 sort 值(若无前项则取首项作为后项)(必须强制转化为 double?,否则无后项时会返回 0,导致逻辑错误)
    double? next_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.sort > prev_sort && c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault()
        : db.dt_dealer.Where(c => c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault();

    if (prev_sort.HasValue && next_sort.HasValue)
    {
        d.sort = (prev_sort.Value + next_sort.Value) / 2;
    }
    if (prev_sort == null && next_sort.HasValue)
    {
        d.sort = next_sort.Value - 1;
    }
    if (prev_sort.HasValue && next_sort == null)
    {
        d.sort = prev_sort.Value + 1;
    }

    db.SaveChanges();

    return Json(new { item = new { id = d.id }, result = new { success = true } });
}

需要注意的是,当往数据库添加新项时,必须将 sort 值设置为已存在的最大 sort 值 +1 或最小 sort 值 -1。

var d = new dt_dealer
{
    name_full = "新建项",
    sort = (db.dt_dealer.Max(c => (double?)c.sort) ?? 0) + 1,
};


xoyozo 8 年前
7,409

ASP.NET MVC 如果从控制器输出的是匿名类的集合,那么绑定到视图时会报错:

“object”未包含“xxx”的定义

简单的解决方法是 JSON 序列化再反序列化,例如:

ViewBag.list = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(q.ToList()));

上面是 ViewBag 的情形,如果是直接 return View(匿名类集合);

那么在视图页面:

@model IEnumerable<dynamic>


xoyozo 8 年前
4,557

Font Awesome 从 5.0 版本开始,在保持经典的 Web Fonts with CSS 外,还提供更现代化,功能更强大的 SVG with JS 部署方式。


升级为 SVG with JS 后,如果部分图标不显示,可能的原因是:

· 部分在老版本中的图标不再 5.0 版本上提供,解决的方法是引用 fa-v4-shims.js;

· 此方式把 <i /> 标签通过 JS 替换为 <svg /> 标签,须要改动 CSS 样式。


如果仍然使用 Web Fonts with CSS,部分图标不显示,可能的原因是:

· 新版本不再推荐以 fa 的 class 引用,对 Solid(实心)、Regular(标准)、Light(简洁)、Brands(品牌)四大分类分别使用新的命名方式:fas、far、fal、fab。具体可以在 Font Awesome 官网上找到相应图标后,点击打开图标详情,复制已经生成的 HTML 代码。fa 将被视为 fas。

xoyozo 8 年前
8,318

1.1 四大特性

MVC

<!doctype html>
<html ng-app>
<body>
    <div ng-controller="HelloAngular">
        <p>{{greeting.text}}, Angular</p>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function HelloAngular($scope) {
            $scope.greeting = {
                text: 'Hello'
            };
        }
    </script>
</body>
</html>

模块化与依赖注入

<!doctype html>
<html ng-app="app">
<body>
    <div ng-controller="HelloAngular">
        <p>{{greeting.text}}, Angular</p>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var myModule = angular.module("app", []);

        myModule.controller("HelloAngular", ['$scope',
            function($scope) {
                $scope.greeting = {
                    text: 'Hello'
                };
            }
        ]);
    </script>
</body>
</html>

双向数据绑定

<!doctype html>
<html ng-app>
<body>
    <input ng-model="greeting.text" />
    <p>{{greeting.text}},AngularJS</p>
    <script src="js/angular-1.3.0.js"></script>
</body>
</html>

指令

<!doctype html>
<html ng-app="app">
<body>
    <hello></hello>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var myModule = angular.module("app", []);
        myModule.directive("hello", function () {
            return {
                restrict: 'E',
                template: '<div>Hi everyone!</div>',
                replace: true
            }
        });
    </script>
</body>
</html>


1.2 开发环境

node.js
JS 运行环境
Bower插件管理(相当于 NuGet)
Grunt代码混淆、合并工具
Sublime代码编辑器(小巧),Zen Coding 快速编写 HTML/CSS 代码
WebStorm代码编辑器(强大)
git 小乌龟版本管理


2.1 MVC

Controller 使用要点:

  • 不要复用 Controller

  • 不要操作 DOM

  • 不要做数据格式化,ng 有表单控件

  • 不要做数据过滤,ng 有 $filter


$scope 作用域

<!doctype html>
<html ng-app>
<head>
    <style>
        .show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
    </style>
</head>
<body>
    <div class="show-scope-demo">
        <div ng-controller="GreetCtrl">
            Hello {{name}}!
        </div>
        <div ng-controller="ListCtrl">
            <ol>
                <li ng-repeat="name in names">
                    {{name}} from {{department}}
                </li>
            </ol>
        </div>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function GreetCtrl($scope, $rootScope) {
            $scope.name = 'World';
            // 在根作用域定义了 department,在 ListCtrl 控制器中也能用
            $rootScope.department = 'Angular';
        }

        function ListCtrl($scope) {
            $scope.names = ['Igor', 'Misko', 'Vojta'];
        }
    </script>
</body>
</html>
<!doctype html>
<html ng-app>
<head>
    <style>
        .show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
    </style>
</head>
<body>
    <div ng-controller="EventController">
        Root scope
        <tt>MyEvent</tt> count: {{count}}
        <ul>
            <li ng-repeat="i in [1]" ng-controller="EventController">
                <button ng-click="$emit('MyEvent')">
                    $emit('MyEvent')
                </button>
                <button ng-click="$broadcast('MyEvent')">
                    $broadcast('MyEvent')
                </button>
                <br>
                Middle scope
                <tt>MyEvent</tt> count: {{count}}
                <ul>
                    <li ng-repeat="item in [1, 2]" ng-controller="EventController">
                        Leaf scope
                        <tt>MyEvent</tt> count: {{count}}
                    </li>
                </ul>
            </li>
        </ul>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        function EventController($scope) {
            $scope.count = 0;
            $scope.$on('MyEvent', function () {
                $scope.count++;
            });
        }

    </script>
</body>
</html>

$emit:向上传播

$broadcast:向下传播

ng-repeat="i in [1, 2]":循环固定项

$scope 是一个树形结构,与 DOM 平行

子 $scope 对象会继承父 $scope 上的属性和方法

每个 Angular 应用只有一个根 $scope,一般位于 ng-app 上

$scope 可以传播事件,类似 DOM 事件,可向上可向下

 

2.2 路由、模块、依赖注入

var bookStoreApp = angular.module('bookStoreApp', [
    'ngRoute', 'ngAnimate', 'bookStoreCtrls', 'bookStoreFilters',
    'bookStoreServices', 'bookStoreDirectives'
]);

bookStoreApp.config(function($routeProvider) {
    $routeProvider.when('/hello', {
        templateUrl: 'tpls/hello.html',
        controller: 'HelloCtrl'
    }).when('/list',{
    	templateUrl:'tpls/bookList.html',
    	controller:'BookListCtrl'
    }).otherwise({
        redirectTo: '/hello'
    })
});


2.3 双向数据绑定

绑定方式1:<p>{{greeting.text}}, Angular</p>

绑定方式2:<p><span ng-bind="greeting.text"></span>, Angular</p>

方式1 在加载 angular.js 之前会将 {{***}} 显示于界面,因此在 index.html 页面建议用方式2,通过模板加载的页面上的绑定可以使用方式1。

<!doctype html>
<html ng-app="UserInfoModule">

<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
</head>

<body>
    <div class="panel panel-primary">
        <div class="panel-heading">
            <div class="panel-title">双向数据绑定</div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-12">
                    <form class="form-horizontal" role="form" ng-controller="UserInfoCtrl">
                        <div class="form-group">
                            <label class="col-md-2 control-label">
                                邮箱:
                            </label>
                            <div class="col-md-10">
                                <input type="email" class="form-control" placeholder="推荐使用126邮箱" ng-model="userInfo.email">
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-md-2 control-label">
                                密码:
                            </label>
                            <div class="col-md-10">
                                <input type="password" class="form-control" placeholder="只能是数字、字母、下划线" ng-model="userInfo.password">
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <div class="checkbox">
                                    <label>
                                        <input type="checkbox" ng-model="userInfo.autoLogin">自动登录
                                    </label>
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <button class="btn btn-default" ng-click="getFormData()">获取Form表单的值</button>
                                <button class="btn btn-default" ng-click="setFormData()">设置Form表单的值</button>
                                <button class="btn btn-default" ng-click="resetForm()">重置表单</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <script src="js/angular-1.3.0.js"></script>
    <script>
        var userInfoModule = angular.module('UserInfoModule', []);
        userInfoModule.controller('UserInfoCtrl', ['$scope',
            function ($scope) {
                $scope.userInfo = {
                    email: "253445528@qq.com",
                    password: "253445528",
                    autoLogin: true
                };
                $scope.getFormData = function () {
                    console.log($scope.userInfo);
                };
                $scope.setFormData = function () {
                    $scope.userInfo = {
                        email: 'damoqiongqiu@126.com',
                        password: 'damoqiongqiu',
                        autoLogin: false
                    }
                };
                $scope.resetForm = function () {
                    $scope.userInfo = {
                        email: "253445528@qq.com",
                        password: "253445528",
                        autoLogin: true
                    };
                }
            }
        ]);
    </script>
</body>
</html>

注意 ng-click 的用法。

ng-class 用法:

ng-class='{bg-danger: isError, bg-success: isSuccess}'

表示当 isError 为 true 时应用 bg-danger 样式,当 isSuccess 为 true 时应用 bg-success 样式。

ng-show / ng-hide 用法:

ng-show='isShow'

表示当 isShow 为 true 时显示,否则隐藏。


2.4 指令(Directive)重要!

路由

Angular 自带的 angular.route.js 不能实现嵌套路由,推荐使用 UI-Router,方便将页面分割成多个 ui-view 实现局部更新。

angular.module("app", []).directive("directiveName", function () {
    return {
        //通过设置项来定义 
    };
});

设置项:

restrict: 'AEMC', // A 属性(默认),E 元素,M 注释,C 样式

template: '<div>xxx</div>', // 替代的 HTML

replace: true, // 替代

transclude: true, // 嵌套,需要 template 配合,例如:

templace: '<div>xxx <div ng-transclude></div></div>', // 原来指令标签中的内容会被放置于 ng-transclude 所在 div 中。

templaceUrl: 'xxx.html', // 使用独立 html 的模板内容

compile: function() { }, // 对模板自身进行转换

link: function() {}, // 操作 DOM、绑定事件监听器


可以在页面中使用 ui-sref 或控制器中 $state.go 来实现跳转(页面更新)

如何传递参数:

一个简单的示例:在一个文章列表页面和一个文章详情页面中实现跳转,需要传递参数文章 id

  1. 在路由配置 state 中加入:params: { "id": null }

  2. 在列表行中绑定 id,格式如:<a ui-sref="app.blog.item({id:row.id})"><span>Edit</span></a>

  3. 在详情页控制器中接收参数:$stateParams,即可用 $stateParams.id 来获取文章 id 值

  4. 如果需要同时更改地址栏 url,并且能够使用该 url 直接打开该文章,那么修改 state 中的 url 为 /item?id,体现为 #/app/blog/item?id=9,也可以用冒号 /item:id,体现为 #/app/blog/item9

参:URL Parameters


scope 的绑定策略:

@:把当前属性作为字符串传递。你还可以绑定来自外层 scope 的值,在属性值中插入 {{ }} 即可

=:与父 scope 中的属性进行双向绑定

&:传递一个来自父 scope 的函数,稍后调用

ERP 类型的系统必备 UI 组件:MiniUIExt JS

互联网/电商型系统必备 UI 组件:KISSY

其它常用组件:AngularUI


2.5 Service 与 Provider

使用 $http.get 获取 JSON 数据

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    <div ng-controller="LoadDataCtrl">
        <ul>
            <li ng-repeat="user in users">
                {{user.name}}
            </li>
        </ul>
    </div>
    <script>
        var app = angular.module("app", []);
        app.controller('LoadDataCtrl', ['$scope', '$http', function ($scope, $http) {
            $http({
                method: 'GET',
                url: 'data.json'
            }).success(function (data, status, headers, config) {
                console.log("success...");
                console.log(data);
                $scope.users = data;
            }).error(function (data, status, headers, config) {
                console.log("error...");
            })
        }]);
    </script>
</body>
</html>

data.json:

[
	{ "name": "a" },
	{ "name": "b" },
	{ "name": "c" }
]

自定义 Service

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    <div ng-controller="ServiceController">
        <label>用户名:</label>
        <input type="text" ng-model="username" placeholder="请输入用户名" />
        <ul ng-show="username">
            <li ng-repeat="user in users">
                {{user.name}}
            </li>
        </ul>
    </div>
    <script>
        var app = angular.module("app", []);

        app.factory('userListService', ['$http', function ($http) {
            var doRequest = function (username, path) {
                return $http({ method: 'GET', url: 'data.json' });
            };
            return {
                userList: function (username) {
                    return doRequest(username, 'userList');
                }
            }
        }]);

        app.controller('ServiceController', ['$scope', '$timeout', 'userListService', function ($scope, $timeout, userListService) {
            var timeout;
            $scope.$watch('username', function (newUserName) {
                if (newUserName) {
                    if (timeout) {
                        $timeout.cancel(timeout);
                    }
                    timeout = $timeout(function () {
                        userListService.userList(newUserName)
                            .success(function (data, status) {
                                $scope.users = data;
                            });
                    }, 350);
                }
            });
        }]);
    </script>
</body>
</html>

知识点:

$timeout 延时,防抖动

userListService 服务调用 $http 服务获取数据,返回一个对象

自定义 Service 命名不要以“$”开头,可以像内置 Service 一样注入的,注入时必须放在内置 Service 之后

Service 是单例的

Service 只要声明,不需要实例化

Service 在整个应用的生命周期中存在,可以用来共享数据


$filter

currency / date / filter / json / limitTo / lowercase / number / orderBy / uppercase

data 例:

{{ 1304375948024 | data }}

{{ 1304375948024 | data:"MM/dd/yyyy @ h:mma" }}

{{ 1304375948024 | data:"yyyy-MM-dd hh:mm:ss" }}

效果:

May 3, 2011

05/03/2011 @ 6:39AM

2011-05-03 06:39:08

自定义 filter:

<!doctype html>
<html ng-app="app">
<head>
    <meta charset="UTF-8">
    <script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
    {{'xoyozo'|filter1}}
    <script>
        var app = angular.module("app", []);
        app.filter('filter1', function () {
            return function (item) {
                return item + 'o(u_u)o';
            }
        });
    </script>
</body>
</html>

其它常用 Service:内置共 24 个

$compile 编译服务

$filter 数据格式化工具,内置了 8 个

$interval

$timeout

$locale

$location

$log

$parse

$http 封装了 ajax


2.6 综合实例 BookStore



xoyozo 8 年前
4,956

函数功能:添加/修改/删除/获取 URL 中的参数(锚点敏感)

/// <summary>
/// 设置 URL 参数(设 value 为 undefined 或 null 删除该参数)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// <param name="value">参数值</param>
/// </summary>
function fn_set_url_param(url, key, value) {
    // 第一步:分离锚点
    var anchor = "";
    var anchor_index = url.indexOf("#");

    if (anchor_index >= 0) {
        anchor = url.substr(anchor_index);
        url = url.substr(0, anchor_index);
    }

    // 第二步:删除参数
    key = encodeURIComponent(key);
    var patt;

    // &key=value
    patt = new RegExp("\\&" + key + "=[^&]*", "gi");
    url = url.replace(patt, "");

    // ?key=value&okey=value  -> ?okey=value
    patt = new RegExp("\\?" + key + "=[^&]*\\&", "gi");
    url = url.replace(patt, "?");

    // ?key=value
    patt = new RegExp("\\?" + key + "=[^&]*$", "gi");
    url = url.replace(patt, "");

    // 第三步:追加参数
    if (value != undefined && value != null) {
        value = encodeURIComponent(value);
        url += (url.indexOf("?") >= 0 ? "&" : "?") + key + "=" + value;
    }

    // 第四步:连接锚点
    url += anchor;

    return url;
}

/// <summary>
/// 获取 URL 参数(无此参数返回 null,多次匹配使用“,”连接)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// </summary>
function fn_get_url_param(url, key) {
    key = encodeURIComponent(key);
    var patt = new RegExp("[?&]" + key + "=([^&#]*)", "gi");

    var values = [];

    while ((result = patt.exec(url)) != null) {
        values.push(decodeURIComponent(result[1]));
    }

    if (values.length > 0) {
        return values.join(",");
    } else {
        return null;
    }
}


调用示例:

var url = window.location.href;

// 设置参数
url = fn_set_url_param(url, 'a', 123);

// 获取参数
console.log(fn_get_url_param, url, 'a');


压缩版:

function fn_set_url_param(b,c,e){var a="";var f=b.indexOf("#");if(f>=0){a=b.substr(f);b=b.substr(0,f)}c=encodeURIComponent(c);var d;d=new RegExp("\\&"+c+"=[^&]*","gi");b=b.replace(d,"");d=new RegExp("\\?"+c+"=[^&]*\\&","gi");b=b.replace(d,"?");d=new RegExp("\\?"+c+"=[^&]*$","gi");b=b.replace(d,"");if(e!=undefined&&e!=null){e=encodeURIComponent(e);b+=(b.indexOf("?")>=0?"&":"?")+c+"="+e}b+=a;return b}
function fn_get_url_param(b,c){c=encodeURIComponent(c);var d=new RegExp("[?&]"+c+"=([^&#]*)","gi");var a=[];while((result=d.exec(b))!=null){a.push(decodeURIComponent(result[1]))}if(a.length>0){return a.join(",")}else{return null}};


xoyozo 9 年前
4,286

本文记录 Newtonsoft.Json 的用法,System.Text.Json 请参此文

如何在序列化时间类型时以“年-月-日 时:分:秒”的格式输出?

默认时间类型将序列化为:2017-11-10T18:48:14.1685763+08:00,我们只要 new 一个 IsoDateTimeConverter 即可改变时间类型的输出格式:

var dtc = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
string b = JsonConvert.SerializeObject(DateTime.Now, dtc);


如何将对象序列化为可友好阅读的字符串?

序列化后的字符串默认是“紧凑型”的,如:{"a":1,"b":2}

可以将 Formatting 指定为 Indented 即可输出格式化后的字符串,例:

var obj = new { a = 1, b = 2 };
string a = JsonConvert.SerializeObject(obj, Formatting.Indented);

结果如下:

{

  "a": 1,

  "b": 2

}


枚举类型输出为字符串,而非索引值

[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]

更多用法参:https://www.cnblogs.com/DomoYao/p/Json.html


不序列化属性

[Newtonsoft.Json.JsonIgnore]

当值为 null 时不序列化

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]


不序列化值为 null 的项

var jSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

JsonConvert.SerializeObject(obj, Formatting.Indented, jSetting);

xoyozo 9 年前
6,386