目前開發所需的gulpfile.js版本 工作流程 for angular 1.x開發 [typescript]->[javascript]->[webpack]->bundle.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
'use strict';

var gulp = require('gulp'),
tsc = require('gulp-typescript'),
inject = require('gulp-inject'),
tsProject = tsc.createProject('tsconfig.json'),
webpack = require('webpack'),
gulpWebpack = require('webpack-stream'),
ngAnnotatePlugin = require('ng-annotate-webpack-plugin'),
path = require('path');

gulp.task('compile-ts', function () {
var sourceTsFiles = ['./app/src/**/*.ts', //path to typescript files
'./app/typings/**/*.ts']; //reference to library .d.ts files


var tsResult = gulp.src(sourceTsFiles)
.pipe(tsc(tsProject));

tsResult.dts.pipe(gulp.dest('./app/dist'));
return tsResult.js.pipe(gulp.dest('./app/dist'));
});

gulp.task('gen-ts-refs', function () {
var target = gulp.src('./app/src/app.d.ts');
var sources = gulp.src(['./app/src/**/*.ts'], { read: false });
return target.pipe(inject(sources, {
starttag: '//{',
endtag: '//}',
transform: function (filepath) {
if (filepath.indexOf('index') > -1) { return; }
if (filepath.indexOf('app.d.ts') > -1) { return; }
return '/// <reference path="../..' + filepath + '" />';
}
})).pipe(gulp.dest('./app/src/'));
});

gulp.task('watch', function () {
gulp.watch(['./app/src/**/*.ts'], ['webpack']);
});

gulp.task('webpack', ['compile-ts'], function () {
return gulp.src('./app/dist/app.js')
.pipe(gulpWebpack({
entry: {
bundled: './app/dist/app.js',
commands: './app/dist/libs.js'
},
output: {
filename: '[name].js',
},
resolve: {
// this tells Webpack where actually to find lodash because you'll need it in the ProvidePlugin
alias: {
lodash: path.resolve(__dirname, './node_modules/lodash'),
angular: path.resolve(__dirname, './node_modules/angular')
},
extensions: ['', '.js']
},
module: {
loaders: [
{ test: /[\/]angular\.js$/, loader: "exports?angular" }
]
},
plugins: [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
// this tells Webpack to provide the "_" variable globally in all your app files as lodash.
new webpack.ProvidePlugin({
_: "lodash",
}),
new ngAnnotatePlugin({
add: true
})
// new webpack.optimize.CommonsChunkPlugin('common.js'),
//new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false
// },
// output: { comments: false }
//})

]
}))
.pipe(gulp.dest('./Scripts'));
})

gulp.task('default', ['watch']);

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
"devDependencies": {
"gulp": "^3.9.0",
"gulp-inject": "^3.0.0",
"gulp-typescript": "^2.10.0",
"gulp-tsd": "^0.0.4",
"tsd": "^0.6.5",
"typescript": "^1.7.5",
"ng-annotate-webpack-plugin": "^0.1.2",
"path": "^0.11.14",
"webpack": "^1.11.0",
"webpack-stream": "^2.1.0"
},
"dependencies": {
"angular": "^1.4.8",
"lodash": "^4.0.0"
}

需要disable visual studio裡面對於typescript的compile,編輯csproj的第一個

1
2
加入這個讓vs不要在Build的時候編譯Typescript
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>

另外需要

1
2
3
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
在這個項目下,增加
<TypeScriptModuleKind>commonjs</TypeScriptModuleKind>

Angular 在 Components之間的值得傳遞方式分割成Inputs和Outputs. 寫法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Components({
....,
inputs:['init'],
outputs:['finish']
})
export class xxx(){
okEvent: EventEmitter<any> = new EventEmitter();

ok(){
// this should match the type define in EventEmitter
this.okEvent.emit('the value want to pass');
}
}

// in another components
<ddd (finish)="finish($event)" [init]="value pass in"></ddd>

$event => will catch the return value

另外一種寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Component, View, Input, Output, EventEmitter } from 'angular2/angular2';

@Components({
....
})
export class xxx(){
@Input() init;
// @Output(alias name)
@Output('finish') okEvent:EventEmitter<Any> = new EventEmitter();


ok(){
// this should match the type define in EventEmitter if use typescript
this.okEvent.emit('the value want to pass');
}
}

// in another components
<ddd (finish)="finish($event)" [init]="value pass in"></ddd>

$event => will catch the return value

After Scaffold from existing datbase, and then add migration at first time.

EF will create something like above. But in first migration will have everything that already existed in database. therefore, delete that file and add migration again. Now this time. you will get an empty migration file. WHy? because ContextModelSnapShot. It seems EF will compare all model files with snapshot file. and find the differences to create migration content file.

And Now it switch to Code first mode. ^^

EF 7 Doc

http://docs.asp.net/en/latest/security/authorization/simple.html 這裡描述怎麼設定頁面授權的方式,可是卻都沒有提到如果說沒授權的人要頁面轉至登入畫面的方式

經過網頁上的查詢及測試後. 在1.0.0-rc1-update1的版本裡,設定方式如下

  1. startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
.....
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCookieAuthentication(options =>
{
options.LoginPath = "/Home/Login";
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
});
....
}

到這裡為止就可以做出跟以前一樣遇到沒有授權的頁面就轉到登入畫面了

###參數說明

重點在於AutomaticAuthenticate 及 AutomaticChallenge 這兩個參數 他的說明如下:

  1. AutomaticAuthenticate: If true the authentication middlleware alter the request user coming in. If false the authentication middleware will only provide identity when explicitly indicated by the AuthenticationScheme.
  2. AutomaticChallenge: If true the authentication middleware should handle automatic challenge. If false the authentication middleware will only alter responses when explicitly indicated by the AuthenticationScheme.

這裡出現另外一個參數 AuthenticationScheme AuthenticationScheme: The AuthenticationScheme in the options corresponds to the logical name for a particular authentication scheme. A different value may be assigned in order to use the same authentication middleware type more than once in a pipepline.

這表示在Controller裡的[Authorize]可以指定AuthenticationScheme, 就可以做出很有彈性的權限設定轉址或是其他後續動作了

1
2
[Authorize(ActiveAuthenticationSchemes ="abc")]
public IActionResult Index(){}

##update 設定頁面授權的方式在這裡 http://docs.asp.net/en/latest/security/authentication/cookie.html

Reference

ASP.NET 5/MVC 6 自訂使用Claim驗証

之前沒有特別留意ngOptions在1.4版裡面修正了一些東西,包含track by的用法 先簡單的描述一下狀況

1
2
3
4
5
6
7
8
9
10
11
12
13
$scope.options = [
{id:1,display:"1"},
{id:2,display:"2"},
{id:3,display:"3"},
{id:4,display:"4"},
{id:5,display:"5"}
] ;

$scope.selected = 3;

// html
<select ng-options="m.id as m.display for m in options"
ng-model="selected"></select>

這種寫法應該算是很常見的用法

但是這樣子的寫法經過1.4版處理後, 仔細去看他的html會變成

1
2
3
4
5
6
7
<select ng-options="m.id as m.display for m in options" ng-model="selected">
<option label="1" value="number:1">1</option>
<option label="2" value="number:2">2</option>
<option label="3" value="number:3" selected="selected">3</option>
<option label="4" value="number:4">4</option>
<option label="5" value="number:5">5</option>
</select>

竟然多了型別…>"<, 這表示如果我的$scope.selected = '3’時,就會選不到東西了

好吧,那如果用track by呢

1
2
<select ng-options="m.id as m.display for m in options track by m.id" 
ng-model="selected"></select>

DOM

1
2
3
4
5
6
7
8
<select ng-options="m.id as m.display for m in options track by m.id" ng-model="selected">
<option value="?" selected="selected"></option>
<option label="1" value="1">1</option>
<option label="2" value="2">2</option>
<option label="3" value="3">3</option>
<option label="4" value="4">4</option>
<option label="5" value="5">5</option>
</select>

這樣子看起來正常多了,但是$scope.selected的值不管是使用 3 or 「3」 都選不到東西. 只有給他options裡面的某一個object他才會被選定。 所以看起來track by是用 for m的m當作選定的值,那 select as label不就沒用了,沒用就拿掉他

1
2
<select ng-options="m.display for m in options track by m.id" 
ng-model="selected"></select>

DOM

1
2
3
4
5
6
7
8
<select ng-options="m.id as m.display for m in options track by m.id" ng-model="selected">
<option value="?" selected="selected"></option>
<option label="1" value="1">1</option>
<option label="2" value="2">2</option>
<option label="3" value="3">3</option>
<option label="4" value="4">4</option>
<option label="5" value="5">5</option>
</select>

看起來都一樣了

結論 用track by: select裡的ng-model會是以object的型態呈現, 不需要再寫select as xxxx了. 不用track by: 就看所表示的select是怎樣的型態,ng-model就是怎樣的型態,但是多了型別的判斷

Classes

Class的組成元素:

  1. Constructor

  2. Prototype methods:

  3. Static methods: 不需要New class就可以使用, 類似C#的Static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}

get area() {
return this.calcArea()
}

calcArea() {
return this.height * this.width;
}

static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;

return Math.sqrt(dx*dx + dy*dy);
}

}

Hoisting: Class並沒有Hoisting特性,所以需要先定義才可以使用,這點須注意

Class inheritance

Class也可以有繼承的性質 範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal { 
constructor(name) {
this.name = name;
}

speak() {
console.log(this.name + ' makes a noise.');
}
}

class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}

Super的用法

Super用來呼叫Parent的function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cat { 
constructor(name) {
this.name = name;
}

speak() {
console.log(this.name + ' makes a noise.');
}
}

class Lion extends Cat {
speak() {
super.speak(); // <= this call Cat's speak function
console.log(this.name + ' roars.');
}
}

Default args

可以將在function的參數給予預設值

1
2
3
function fnWithDefaultArg(a,b=[]){
// some codes
}

Spread operator

The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.

1
2
3
4
5
function fn(a,b,c){
// some codes
}
var args = [1,2,3];
fn(...args); <= spread operator

A more powerful array literal

1
2
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes']; // ["head", "shoulders", "knees", "and", "toes"]

A better push

1
2
3
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

現在SPA的網站越來越盛行,下載檔案的功能在ajax下是有點麻煩,但是感謝HTML5下的Blob功能。網路上就有相對應的js功能出來 所以,引用這位大大的程式 (https://github.com/eligrey/FileSaver.js),其相關限制都有在該專案上描述

所以使用方法如下 (Client端)

1
2
3
4
5
6
7
8
9
10
$http.post('api url', query, { responseType: 'arraybuffer' }).then(function (response) {        
var filename = (filename);
var expectedMediaType = (file-Content-Type);
openSaveAsDialog(filename, response.data, expectedMediaType);
});

function openSaveAsDialog(filename, content, mediaType) {
var blob = new Blob([content], { type: mediaType });
saveAs(blob, filename);
}

重點 : responseType 要設定為 arraybuffer

(Server端) Webapi要回傳的httpresponseMessage內容如下, 不好意思程式碼是VB, 因為這個專案是用VB開發的,但是基本觀念的一樣的

1
2
3
4
5
6
7
8
9
10
Dim response As HttpResponseMessage = New HttpResponseMessage()
' _ms 是 MemoryStream, 這裡是因為要將NPOI所產生的excel檔做下載, 然而我將NPOI所產生出來的東西
' 存入到 MemoryStream裡, 重點是ByteArrayContent
Dim _filename as string = (filename)
If _ms IsNot Nothing Then
response.Content = New ByteArrayContent(_ms.ToArray())
End If
response.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/octet-stream")
response.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment") With {.FileName = HttpUtility.UrlPathEncode(_filename)}
Return Task.FromResult(response)

以上的動作就可以讓ajax的call直接做下載檔案的動作,就不用另外產生一個form然後做post到新視窗後再下載了 ^^

Destructuring

可以將值從陣列或是物件裡取出並設定到變數上

syntax

1
2
3
[a, b] = [1, 2]
[a, b, ...rest] = [1, 2, 3, 4, 5]
{a, b} = {a:1, b:2}

Destructuring arrays

1
2
3
4
5
6
7
8
9
var foo = ["one", "two", "three"];

// without destructuring
var one = foo[0];
var two = foo[1];
var three = foo[2];

// with destructuring
var [one, two, three] = foo;

其他用法 1.Multiple-value returns

1
2
3
4
5
6
function f() {
return [1, 2];
}
var a, b;
[a, b] = f();
console.log("A is " + a + " B is " + b);

2.Ignoring some returned values

1
2
3
4
5
6
7
function f() {
return [1, 2, 3];
}
var a, b;
[a, ,b] = f();
console.log("A is " + a + " B is " + b);
// A is 1 B is 3

3.衍伸用法: Pulling values from a regular expression match

1
2
3
4
5
6
var url = "https://developer.mozilla.org/en-US/Web/JavaScript";

var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
var [, protocol, fullhost, fullpath] = parsedURL;

console.log(protocol); // logs "https"

Destructuring objects

1
2
3
4
5
var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true

另外一種用法, 將物件裡的值設定到新的變數名稱上

1
2
3
4
5
6
7
8
9
// syntax
// {object.propertyName: VariableName} = object

// Assign new variable names
var o = {p: 42, q: true};
var {p: foo, q: bar} = o;

console.log(foo); // 42
console.log(bar); // true

衍生用法: Function argument defaults

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var [missing = true] = [];
console.log(missing);
// true

var { message: msg = "Something went wrong" } = {};
console.log(msg);
// "Something went wrong"

var { x = 3 } = {};
console.log(x);
// 3

function removeBreakpoint({ url, line, column }={}) {
// ...
}
1
2
3
4
5
6
7
8
9
10
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) 
{
console.log(size, cords, radius);
// do some chart drawing
}

drawES6Chart({
cords: { x: 18, y: 30 },
radius: 30
});

另外一種情境 Module (non-ES6) loading

1
const { Loader, main } = require('toolkit/loader');

REF: Several demos and usages for ES6 destructuring.

懶得再去微軟的網站找了,所以在此備註

CTRL+SHIFT+G 在交談中切換重要屬性 CTRL+D, DEL 刪除交談 退格鍵 封存交談 CTRL+Q 標示為已讀取 CTRL+U 標示為未讀取 CTRL+R 回覆目前郵件 CTRL+SHIFT+R 全部回覆 CTRL+F 轉寄目前郵件 CTRL+E,F3 搜尋 Alt+C 接受會議邀請 Alt+D 拒絕會議邀請 Alt+N 暫訂會議邀請 CTRL+N 建立新的電子郵件 CTRL+SHIFT+M 建立新的電子郵件 CTRL+SHIFT+V 移到檢視 CTRL+1 切換至 [郵件] CTRL+2 切換至 [行事曆] CTRL+SHIFT+I 切換至 [收件匣] CTRL+SHIFT+O 切換至 [寄件匣]

ALT+I 新增附件 CTRL+M 手動同步帳戶 F9 手動同步帳戶 CTRL+ + 放大 CTRL+ - 縮小 ALT+S 傳送郵件 Ctrl + Enter 傳送郵件 F6 在區域間移動 F7 切換鍵盤瀏覽