Merge remote-tracking branch 'origin/master'
# Conflicts: # public/_data.json # public/docs/ts/latest/_data.json # public/docs/ts/latest/guide/router.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/template-syntax.jade # public/news.jade # public/resources/_data.json # public/resources/index.ejs # public/resources/js/controllers/resources-controller.js
This commit is contained in:
commit
0dd2932a2e
|
@ -671,9 +671,9 @@ gulp.task('lint', function() {
|
|||
'!./public/docs/_examples/_protractor/**/*',
|
||||
'!./public/docs/_examples/**/typings/**/*',
|
||||
'!./public/docs/_examples/**/typings-ng1/**/*',
|
||||
'!./public/docs/_examples/**/build/**/*',
|
||||
// temporary until codelyzer is fixed mgechev/codelyzer#60
|
||||
'!./public/docs/_examples/animations/ts/app/hero.service.ts'
|
||||
|
||||
])
|
||||
.pipe(tslint({
|
||||
rulesDirectory: ['node_modules/codelyzer'],
|
||||
|
|
|
@ -477,6 +477,13 @@
|
|||
"twitter": "rexebin",
|
||||
"bio": "Rex is a full-stack developer. He maintains the Angular.cn website with his old pal Ralph Wang and he plays a key role in bridging between the Chinese Angular community and the world-wide community. He loves playing with flashy new technologies and enjoys the challenge of mastering new skills. His biggest challenge to date is figuring out how to sooth a crying 4-month-old baby.",
|
||||
"type": "Community"
|
||||
},
|
||||
"ralph": {
|
||||
"name": "Ralph Wang",
|
||||
"picture": "/resources/images/bios/ralph.jpg",
|
||||
"twitter": "ralph_wang_gde",
|
||||
"bio": "Ralph(Zhicheng Wang) is a senior consultant at ThoughWorks and also a GDE. He is a technology enthusiast and he is a passionate advocate of “Simplicity, Professionalism and Sharing”. In his eighteen years of R&D career, he worked as tester, R&D engineer, project manager, product manager and CTO. He is looking forward to the birth of his baby.",
|
||||
"type": "Community"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,17 +32,5 @@
|
|||
},
|
||||
"presskit": {
|
||||
"title": "宣传资料"
|
||||
},
|
||||
"books": {
|
||||
"title": "书籍"
|
||||
},
|
||||
"training": {
|
||||
"title": "培训"
|
||||
},
|
||||
"communities": {
|
||||
"title": "社区"
|
||||
},
|
||||
"tooling": {
|
||||
"title": "工具与库"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,17 +25,17 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@angular/common": "2.0.0-rc.3",
|
||||
"@angular/compiler": "2.0.0-rc.3",
|
||||
"@angular/core": "2.0.0-rc.3",
|
||||
"@angular/forms": "0.1.1",
|
||||
"@angular/http": "2.0.0-rc.3",
|
||||
"@angular/platform-browser": "2.0.0-rc.3",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
|
||||
"@angular/router": "3.0.0-alpha.8",
|
||||
"@angular/common": "2.0.0-rc.4",
|
||||
"@angular/compiler": "2.0.0-rc.4",
|
||||
"@angular/core": "2.0.0-rc.4",
|
||||
"@angular/forms": "0.2.0",
|
||||
"@angular/http": "2.0.0-rc.4",
|
||||
"@angular/platform-browser": "2.0.0-rc.4",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
|
||||
"@angular/router": "3.0.0-beta.1",
|
||||
"@angular/router-deprecated": "2.0.0-rc.2",
|
||||
"@angular/upgrade": "2.0.0-rc.3",
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"@angular/upgrade": "2.0.0-rc.4",
|
||||
"angular2-in-memory-web-api": "0.0.14",
|
||||
"bootstrap": "^3.3.6",
|
||||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
|
|
|
@ -7,23 +7,23 @@
|
|||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@angular/common": "2.0.0-rc.3",
|
||||
"@angular/compiler": "2.0.0-rc.3",
|
||||
"@angular/core": "2.0.0-rc.3",
|
||||
"@angular/forms": "0.1.1",
|
||||
"@angular/http": "2.0.0-rc.3",
|
||||
"@angular/platform-browser": "2.0.0-rc.3",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
|
||||
"@angular/router": "3.0.0-alpha.8",
|
||||
"@angular/common": "2.0.0-rc.4",
|
||||
"@angular/compiler": "2.0.0-rc.4",
|
||||
"@angular/core": "2.0.0-rc.4",
|
||||
"@angular/forms": "0.2.0",
|
||||
"@angular/http": "2.0.0-rc.4",
|
||||
"@angular/platform-browser": "2.0.0-rc.4",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
|
||||
"@angular/router": "3.0.0-beta.1",
|
||||
"@angular/router-deprecated": "2.0.0-rc.2",
|
||||
"@angular/upgrade": "2.0.0-rc.3",
|
||||
"@angular/upgrade": "2.0.0-rc.4",
|
||||
|
||||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "0.1.3",
|
||||
"rxjs": "5.0.0-beta.6",
|
||||
"zone.js": "0.6.12",
|
||||
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"angular2-in-memory-web-api": "0.0.14",
|
||||
"bootstrap": "^3.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -11,16 +11,16 @@
|
|||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@angular/common": "2.0.0-rc.3",
|
||||
"@angular/compiler": "2.0.0-rc.3",
|
||||
"@angular/core": "2.0.0-rc.3",
|
||||
"@angular/forms": "0.1.1",
|
||||
"@angular/http": "2.0.0-rc.3",
|
||||
"@angular/platform-browser": "2.0.0-rc.3",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
|
||||
"@angular/router": "3.0.0-alpha.8",
|
||||
"@angular/common": "2.0.0-rc.4",
|
||||
"@angular/compiler": "2.0.0-rc.4",
|
||||
"@angular/core": "2.0.0-rc.4",
|
||||
"@angular/forms": "0.2.0",
|
||||
"@angular/http": "2.0.0-rc.4",
|
||||
"@angular/platform-browser": "2.0.0-rc.4",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
|
||||
"@angular/router": "3.0.0-beta.1",
|
||||
"@angular/router-deprecated": "2.0.0-rc.2",
|
||||
"@angular/upgrade": "2.0.0-rc.3",
|
||||
"@angular/upgrade": "2.0.0-rc.4",
|
||||
|
||||
"systemjs": "0.19.27",
|
||||
"core-js": "^2.4.0",
|
||||
|
@ -28,7 +28,7 @@
|
|||
"rxjs": "5.0.0-beta.6",
|
||||
"zone.js": "^0.6.12",
|
||||
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"angular2-in-memory-web-api": "0.0.14",
|
||||
"bootstrap": "^3.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
'use strict';
|
||||
|
||||
describe('Security E2E Tests', () => {
|
||||
beforeAll(function() { browser.get(''); });
|
||||
beforeAll(() => browser.get(''));
|
||||
|
||||
it('sanitizes innerHTML', () => {
|
||||
let interpolated = element(By.className('e2e-inner-html-interpolated'));
|
||||
|
@ -13,13 +14,23 @@ describe('Security E2E Tests', () => {
|
|||
expect(bold.getText()).toContain('Syntax');
|
||||
});
|
||||
|
||||
it('escapes untrusted URLs', () => {
|
||||
let untrustedUrl = element(By.className('e2e-dangerous-url'));
|
||||
expect(untrustedUrl.getAttribute('href')).toMatch(/^unsafe:javascript/);
|
||||
});
|
||||
|
||||
it('binds trusted URLs', () => {
|
||||
let dangerousUrl = element(By.className('e2e-dangerous-url'));
|
||||
expect(dangerousUrl.getAttribute('href')).toMatch(/^javascript:alert/);
|
||||
let trustedUrl = element(By.className('e2e-trusted-url'));
|
||||
expect(trustedUrl.getAttribute('href')).toMatch(/^javascript:alert/);
|
||||
});
|
||||
|
||||
it('escapes untrusted resource URLs', () => {
|
||||
let iframe = element(By.className('e2e-iframe-untrusted-src'));
|
||||
expect(iframe.getAttribute('src')).toBe('');
|
||||
});
|
||||
|
||||
it('binds trusted resource URLs', () => {
|
||||
let iframe = element(By.className('e2e-iframe'));
|
||||
let iframe = element(By.className('e2e-iframe-trusted-src'));
|
||||
expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import { BypassSecurityComponent } from './bypass-security.component';
|
|||
import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>Security</h1>
|
||||
<inner-html-binding></inner-html-binding>
|
||||
|
@ -14,7 +14,7 @@ import { InnerHtmlBindingComponent } from './inner-html-binding.component';
|
|||
directives: [
|
||||
BypassSecurityComponent,
|
||||
InnerHtmlBindingComponent,
|
||||
],
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
<h3>Bypass Security Component</h3>
|
||||
|
||||
<!--#docregion dangerous-url -->
|
||||
<h4>A dangerous URL:</h4>
|
||||
<p><a class="e2e-dangerous-url" [href]="dangerousUrl">Click me.</a></p>
|
||||
<h4>A untrusted URL:</h4>
|
||||
<p><a class="e2e-dangerous-url" [href]="dangerousUrl">Click me</a></p>
|
||||
<h4>A trusted URL:</h4>
|
||||
<p><a class="e2e-trusted-url" [href]="trustedUrl">Click me</a></p>
|
||||
<!--#enddocregion dangerous-url -->
|
||||
|
||||
<!--#docregion iframe-videoid -->
|
||||
<h4>Resource URL:</h4>
|
||||
<p><label>Showing: <input (input)="updateVideoUrl($event.target.value)"></label></p>
|
||||
<iframe class="e2e-iframe" width="640" height="390" [src]="videoUrl"></iframe>
|
||||
<p>Trusted:</p>
|
||||
<iframe class="e2e-iframe-trusted-src" width="640" height="390" [src]="videoUrl"></iframe>
|
||||
<p>Untrusted:</p>
|
||||
<iframe class="e2e-iframe-untrusted-src" width="640" height="390" [src]="dangerousVideoUrl"></iframe>
|
||||
<!--#enddocregion iframe-videoid -->
|
||||
|
||||
<!--#enddocregion -->
|
||||
|
|
|
@ -8,14 +8,18 @@ import { DomSanitizationService, SafeResourceUrl, SafeUrl } from '@angular/platf
|
|||
templateUrl: 'app/bypass-security.component.html',
|
||||
})
|
||||
export class BypassSecurityComponent {
|
||||
dangerousUrl: SafeUrl;
|
||||
dangerousUrl: string;
|
||||
trustedUrl: SafeUrl;
|
||||
dangerousVideoUrl: string;
|
||||
videoUrl: SafeResourceUrl;
|
||||
|
||||
// #docregion trust-url
|
||||
constructor(private sanitizer: DomSanitizationService) {
|
||||
// javascript: URLs are dangerous if attacker controlled. Angular sanitizes them in data
|
||||
// binding, but we can explicitly tell Angular to trust this value:
|
||||
this.dangerousUrl = sanitizer.bypassSecurityTrustUrl('javascript:alert("Hi there")');
|
||||
// javascript: URLs are dangerous if attacker controlled.
|
||||
// Angular sanitizes them in data binding, but we can
|
||||
// explicitly tell Angular to trust this value:
|
||||
this.dangerousUrl = 'javascript:alert("Hi there")';
|
||||
this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);
|
||||
// #enddocregion trust-url
|
||||
this.updateVideoUrl('PUBnlbjZFAI');
|
||||
}
|
||||
|
@ -23,11 +27,12 @@ export class BypassSecurityComponent {
|
|||
// #docregion trust-video-url
|
||||
updateVideoUrl(id: string) {
|
||||
// Appending an ID to a YouTube URL is safe.
|
||||
// Always make sure to construct SafeValue objects as close as possible to the input data, so
|
||||
// Always make sure to construct SafeValue objects as
|
||||
// close as possible to the input data, so
|
||||
// that it's easier to check if the value is safe.
|
||||
this.dangerousVideoUrl = 'https://www.youtube.com/embed/' + id;
|
||||
this.videoUrl =
|
||||
this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/' + id);
|
||||
this.sanitizer.bypassSecurityTrustResourceUrl(this.dangerousVideoUrl);
|
||||
}
|
||||
// #enddocregion trust-video-url
|
||||
}
|
||||
// #enddocregion
|
||||
|
|
|
@ -11,4 +11,3 @@ export class InnerHtmlBindingComponent {
|
|||
// E.g. a user/attacker controlled value from a URL.
|
||||
htmlSnippet = 'Template <script>alert("0wned")</script> <b>Syntax</b>';
|
||||
}
|
||||
// #enddocregion inner-html-controller
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- #docregion -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular Content Security</title>
|
||||
|
@ -21,6 +21,6 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<app-root>Loading...</app-root>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
(function(global) {
|
||||
|
||||
var ngVer = '@2.0.0-rc.3'; // lock in the angular package version; do not let it float to current!
|
||||
var routerVer = '@3.0.0-alpha.7'; // lock router version
|
||||
var formsVer = '@0.1.1'; // lock forms version
|
||||
var ngVer = '@2.0.0-rc.4'; // lock in the angular package version; do not let it float to current!
|
||||
var routerVer = '@3.0.0-beta.1'; // lock router version
|
||||
var formsVer = '@0.2.0'; // lock forms version
|
||||
var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides
|
||||
|
||||
//map tells the System loader where to look for things
|
||||
|
|
|
@ -589,18 +589,9 @@ bindon-ngModel
|
|||
<div *ngFor="let hero of heroes trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
|
||||
</div>
|
||||
|
||||
<p>with <i>*ngForTrackBy</i></p>
|
||||
<div class="box">
|
||||
<!-- #docregion NgForTrackBy-2 -->
|
||||
<div *ngFor="let hero of heroes" *ngForTrackBy="trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
|
||||
<!-- #enddocregion NgForTrackBy-2 -->
|
||||
</div>
|
||||
|
||||
<p>with <i>generic</i> trackById function</p>
|
||||
<div class="box">
|
||||
<!-- #docregion NgForTrackBy-3 -->
|
||||
<div *ngFor="let hero of heroes" *ngForTrackBy="trackById">({{hero.id}}) {{hero.fullName}}</div>
|
||||
<!-- #enddocregion NgForTrackBy-3 -->
|
||||
<div *ngFor="let hero of heroes, trackBy:trackById">({{hero.id}}) {{hero.fullName}}</div>
|
||||
</div>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { HeroesComponent } from './heroes.component';
|
|||
|
||||
const routes: RouterConfig = [
|
||||
{
|
||||
path: '/heroes',
|
||||
path: 'heroes',
|
||||
component: HeroesComponent
|
||||
}
|
||||
];
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
/* tslint:disable */
|
||||
// #docregion routestuff
|
||||
import { ApplicationRef } from '@angular/core';
|
||||
import { LocationStrategy } from '@angular/common';
|
||||
import { Directive } from '@angular/core';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import {
|
||||
ROUTER_PROVIDERS,
|
||||
Router,
|
||||
RouterLink,
|
||||
RootRouter,
|
||||
RouteRegistry,
|
||||
ROUTER_PRIMARY_COMPONENT
|
||||
} from '@angular/router-deprecated';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
addProviders,
|
||||
inject,
|
||||
it,
|
||||
expect,
|
||||
MockApplicationRef
|
||||
// MockApplicationRef
|
||||
} from '@angular/core/testing';
|
||||
import { MockLocationStrategy } from '@angular/common/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixture
|
||||
} from '@angular/compiler/testing';
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { AppComponent } from '../app.component';
|
||||
import { PhoneListComponent } from './phone-list.component';
|
||||
|
@ -27,6 +30,11 @@ import { Phone, PhoneData } from '../core/phone/phone.service';
|
|||
|
||||
// #enddocregion routestuff
|
||||
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
inputs: ['routeParams: routerLink', 'target: target']
|
||||
})
|
||||
class RouterLinkMock {}
|
||||
|
||||
class MockPhone extends Phone {
|
||||
query(): Observable<PhoneData[]> {
|
||||
|
@ -41,19 +49,22 @@ describe('PhoneList', () => {
|
|||
|
||||
// #docregion routestuff
|
||||
|
||||
beforeEachProviders(() => [
|
||||
{ provide: Phone, useClass: MockPhone},
|
||||
HTTP_PROVIDERS,
|
||||
ROUTER_PROVIDERS,
|
||||
{ provide: ApplicationRef, useClass: MockApplicationRef },
|
||||
addProviders([
|
||||
RouteRegistry,
|
||||
{ provide: Router, useClass: RootRouter },
|
||||
{ provide: ROUTER_PRIMARY_COMPONENT, useValue: AppComponent },
|
||||
{ provide: LocationStrategy, useClass: MockLocationStrategy},
|
||||
{ provide: Location, useClass: SpyLocation},
|
||||
{ provide: Phone, useClass: MockPhone},
|
||||
HTTP_PROVIDERS
|
||||
]);
|
||||
// #enddocregion routestuff
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneListComponent)
|
||||
return tcb
|
||||
.overrideDirective(AppComponent, RouterLink, RouterLinkMock)
|
||||
.overrideDirective(PhoneListComponent, RouterLink, RouterLinkMock)
|
||||
.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
|
@ -69,7 +80,10 @@ describe('PhoneList', () => {
|
|||
|
||||
it('should set the default value of orderProp model',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneListComponent)
|
||||
return tcb
|
||||
.overrideDirective(AppComponent, RouterLink, RouterLinkMock)
|
||||
.overrideDirective(PhoneListComponent, RouterLink, RouterLinkMock)
|
||||
.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/common": "2.0.0-rc.3",
|
||||
"@angular/compiler": "2.0.0-rc.3",
|
||||
"@angular/core": "2.0.0-rc.3",
|
||||
"@angular/forms": "0.1.1",
|
||||
"@angular/http": "2.0.0-rc.3",
|
||||
"@angular/platform-browser": "2.0.0-rc.3",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
|
||||
"@angular/router": "3.0.0-alpha.8",
|
||||
"@angular/common": "2.0.0-rc.4",
|
||||
"@angular/compiler": "2.0.0-rc.4",
|
||||
"@angular/core": "2.0.0-rc.4",
|
||||
"@angular/forms": "0.2.0",
|
||||
"@angular/http": "2.0.0-rc.4",
|
||||
"@angular/platform-browser": "2.0.0-rc.4",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
|
||||
"@angular/router": "3.0.0-beta.1",
|
||||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "0.1.2",
|
||||
"rxjs": "5.0.0-beta.6",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"icon": "home",
|
||||
"title": "Angular Docs",
|
||||
"menuTitle": "Docs Home",
|
||||
"banner": "Welcome to <b>Angular in JavaScript</b>! The current Angular 2 release is <b>rc.3</b>. Please consult the <a href='https://github.com/angular/angular/blob/master/CHANGELOG.md' target='_blank'> Change Log</a> about recent enhancements, fixes, and breaking changes."
|
||||
"banner": "Welcome to <b>Angular in JavaScript</b>! The current Angular 2 release is <b>rc.4</b>. Please consult the <a href='https://github.com/angular/angular/blob/master/CHANGELOG.md' target='_blank'> Change Log</a> about recent enhancements, fixes, and breaking changes."
|
||||
},
|
||||
|
||||
"quickstart": {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"icon": "home",
|
||||
"title": "Angular文档",
|
||||
"menuTitle": "文档首页",
|
||||
"banner": "欢迎来到 <b>Angular in TypeScript</b>! 当前的Angular版本是 <b>rc.3</b>。请参考<a href='https://github.com/angular/angular/blob/master/CHANGELOG.md' target='_blank'>变更记录</a>、最新功能、BUG修复和重大的破坏性更改。"
|
||||
"banner": "欢迎来到 <b>Angular in TypeScript</b>! 当前的Angular版本是 <b>rc.4</b>。请参考<a href='https://github.com/angular/angular/blob/master/CHANGELOG.md' target='_blank'>变更记录</a>、最新功能、BUG修复和重大的破坏性更改。"
|
||||
},
|
||||
|
||||
"cli-quickstart": {
|
||||
|
|
|
@ -3,7 +3,7 @@ include ../_util-fns
|
|||
.alert.is-important
|
||||
|
||||
:marked
|
||||
The Component Router is in alpha release. This is the recommended Angular 2 router and supersedes
|
||||
The Component Router is in beta release. This is the recommended Angular 2 router and supersedes
|
||||
the earlier *deprecated beta* and *v2* routers.
|
||||
|
||||
组件路由器已经进入了Alpha阶段,推荐在Angular 2中使用此路由器,以取代早前*废弃的beta版*及*第二版*路由器。
|
||||
|
@ -1612,7 +1612,7 @@ h3#nav-to-list 导航回列表组件
|
|||
除非我们能学到点新东西,否则这种练习就没啥亮点。
|
||||
不过,我们已经有了一些新主意和新技巧:
|
||||
|
||||
* We'd like our route URLs to branch in to child route trees that reflect the component treese in our feature areas.
|
||||
* We'd like our route URLs to branch in to child route trees that reflect the component trees in our feature areas.
|
||||
|
||||
* 我们希望把这些路由地址组织成一棵子路由树,它应该能反映本特性区中组件树的结构。
|
||||
|
||||
|
@ -1909,8 +1909,8 @@ code-example(format="").
|
|||
|
||||
:marked
|
||||
Since we only want to redirect when our path specifically matches `''`, we've added an extra configuration
|
||||
to our route using `terminal: true`. Mainly for redirects, the `terminal` property gives us more control over
|
||||
when the router should continue matching our URL against our defined routes.
|
||||
to our route using `terminal: true`. Mainly for redirects, the `terminal` property tells the router
|
||||
whether or not it should continue matching our URL against the rest of our defined routes.
|
||||
|
||||
由于我们希望只有在路径明确的匹配到`''`时才重定向,所以我们往路由中添加了一个额外的配置项:`terminal: true`。
|
||||
主要是为了重定向,`terminal`属性是用来控制路由器是否应该继续匹配URL和我们定义的路由。(译者注:`terminal`设置为`true`时,该路由的优先级别就低于其他路由,只有在所有其他路由都不匹配的时候才最后匹配这个路由。主要是为了标示专用于重定向的路由而设计的。)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
block includes
|
||||
include ../_util-fns
|
||||
:marked
|
||||
Web application security has many aspects. This documentation describes Angular's built in
|
||||
Web application security has many aspects. This chapter describes Angular's built in
|
||||
protections against common web application vulnerabilities and attacks, such as Cross Site
|
||||
Scripting Attacks. It does not cover application level security, such as authentication (_Who is
|
||||
this user?_) or authorization (_What can this user do?_).
|
||||
|
@ -97,7 +97,7 @@ h2#xss 防范跨站脚本(XSS)攻击
|
|||
|
||||
:marked
|
||||
[Cross-Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers
|
||||
to inject malicious code into web pages. Such code can then for example steal user's data (in
|
||||
to inject malicious code into web pages. Such code can then, for example, steal user's data (in
|
||||
particular their login data), or perform actions impersonating the user. This is one of the most
|
||||
common attacks on the web.
|
||||
|
||||
|
@ -151,7 +151,7 @@ h2#xss 防范跨站脚本(XSS)攻击
|
|||
|
||||
Angular定义了四个安全环境:HTML,样式,URL,和资源URL。
|
||||
|
||||
* HTML is used when interpreting a value as HTML, e.g. when binding to `innerHtml`
|
||||
* HTML is used when interpreting a value as HTML, e.g., when binding to `innerHtml`
|
||||
|
||||
* HTML:值需要被解释为HTML时使用,比如当绑定到`innerHTML`时。
|
||||
|
||||
|
@ -163,7 +163,7 @@ h2#xss 防范跨站脚本(XSS)攻击
|
|||
|
||||
* URL:值需要被用作URL属性时使用,比如`<a href>`。
|
||||
|
||||
* Resource URLs are URLs that will be loaded and executed as code, e.g. in `<script src>`
|
||||
* Resource URLs are URLs that will be loaded and executed as code, e.g., in `<script src>`
|
||||
|
||||
* 资源URL:值需要被当做代码而加载并执行时使用,比如`<script src>`中的URL。
|
||||
|
||||
|
@ -183,7 +183,7 @@ h2#xss 防范跨站脚本(XSS)攻击
|
|||
|
||||
下面的例子绑定了`htmlSnippet`的值,一次把它放进插值表达式里,另一次把它绑定到元素的`innerHTML`属性上。
|
||||
|
||||
+makeExample('security/ts/app/inner-html-binding.component.html')(format=".")
|
||||
+makeExample('app/inner-html-binding.component.html')
|
||||
|
||||
:marked
|
||||
Interpolated content is always escaped - the HTML is not interpreted, and the browser displays
|
||||
|
@ -198,7 +198,7 @@ h2#xss 防范跨站脚本(XSS)攻击
|
|||
如果希望这段HTML被正常解释,就必须绑定到一个HTML属性上,比如`innerHTML`。但是如果把一个可能被攻击者控制的值绑定到`innerHTML`就会导致XSS漏洞。
|
||||
比如,包含在`<script>`标签的代码就会被执行。
|
||||
|
||||
+makeExample('security/ts/app/inner-html-binding.component.ts', 'inner-html-controller')(format=".")
|
||||
+makeExcerpt('app/inner-html-binding.component.ts ()', 'inner-html-controller')
|
||||
|
||||
:marked
|
||||
Angular recognizes the value as unsafe, and automatically sanitizes it. It removes the `<script>`
|
||||
|
@ -227,8 +227,7 @@ figure.image-display
|
|||
|
||||
### 内容安全策略
|
||||
|
||||
A [Content Security Policy (CSP)]
|
||||
(http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
||||
A [Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth
|
||||
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
|
||||
`Content-Security-Policy` HTTP header.
|
||||
|
||||
|
@ -257,7 +256,7 @@ figure.image-display
|
|||
### 服务器端XSS保护
|
||||
|
||||
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
|
||||
Angular application is the same as injecting executable code (e.g. JavaScript) into the
|
||||
Angular application is the same as injecting executable code into the
|
||||
application; it gives the attacker full control over the application. To prevent this, make sure
|
||||
to use a templating language that automatically escapes values to prevent XSS vulnerabilities on
|
||||
the server. Do not generate Angular templates on the server side using a templating language, this
|
||||
|
@ -301,15 +300,16 @@ h2#bypass-security-apis 信任安全的值
|
|||
|
||||
记住,一个值是否安全取决于它所在的环境,所以你要为这个值按预定的用法选择正确的环境。假设下面的模板需要把`javascript.alert(...)`方法绑定到URL。
|
||||
|
||||
+makeExample('security/ts/app/bypass-security.component.html', 'dangerous-url')(format=".")
|
||||
+makeExcerpt('app/bypass-security.component.html ()', 'dangerous-url')
|
||||
|
||||
:marked
|
||||
Normally, Angular would automatically sanitize the URL and disable the dangerous code. To prevent
|
||||
Normally, Angular automatically sanitizes the URL, disables the dangerous code and,
|
||||
in development mode, logs this action to the console. To prevent
|
||||
this, we can mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
|
||||
|
||||
通常,Angular会自动无害化这个URL并禁止危险的代码。为了防止这种行为,我们可以调用`bypassSecurityTrustUrl`把这个URL值标记为一个可信任的URL:
|
||||
|
||||
+makeExample('security/ts/app/bypass-security.component.ts', 'trust-url')(format=".")
|
||||
+makeExcerpt('app/bypass-security.component.ts ()', 'trust-url')
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/security/bypass-security-component.png'
|
||||
|
@ -319,7 +319,7 @@ figure.image-display
|
|||
If we need to convert user input into a trusted value, it can be convenient to do so in a
|
||||
controller method. The template below allows users to enter a YouTube video ID, and load the
|
||||
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
|
||||
context, because an untrusted source can e.g. smuggle in file downloads that unsuspecting users
|
||||
context, because an untrusted source can, e.g., smuggle in file downloads that unsuspecting users
|
||||
would execute. So we call a method on the controller to construct a trusted video URL, which
|
||||
Angular then allows binding into `<iframe src>`.
|
||||
|
||||
|
@ -327,9 +327,9 @@ figure.image-display
|
|||
然后把相应的视频加载到`<iframe>`中。`<iframe src>`是一个“资源URL”的安全环境,因为不可信的源码可能作为文件下载到本地,被毫无防备的用户执行。
|
||||
所以我们要调用一个控制器方法来构造一个新的、可信任的视频URL,然后把它绑定到`<iframe src>`。
|
||||
|
||||
+makeExample('security/ts/app/bypass-security.component.html', 'iframe-videoid')(format=".")
|
||||
+makeExcerpt('app/bypass-security.component.html ()', 'iframe-videoid')
|
||||
|
||||
+makeExample('security/ts/app/bypass-security.component.ts', 'trust-video-url')(format=".")
|
||||
+makeExcerpt('app/bypass-security.component.ts ()', 'trust-video-url')
|
||||
|
||||
.l-main-section
|
||||
h2#http HTTP-level Vulnerabilities
|
||||
|
@ -350,16 +350,16 @@ h3#xsrf 跨站请求伪造(XSRF)
|
|||
|
||||
:marked
|
||||
In a Cross-site Request Forgery (XSRF or CSRF), an attacker tricks the user into visiting a
|
||||
_different_ page, and has them e.g. submit a form that sends a request to your application's
|
||||
_different_ page, and has them, e.g., submit a form that sends a request to your application's
|
||||
web server. If the user is logged into your application, the browser will send authentication
|
||||
cookies, and the attacker could - for example - cause a bank transfer in the user's name with
|
||||
cookies, and the attacker could — for example — cause a bank transfer in the user's name with
|
||||
the right request.
|
||||
|
||||
在跨站请求伪造(XSRF或CSFR)中,一个攻击者会欺骗用户,让他们访问_另一个_页面,并提交一个表单,
|
||||
向你应用程序的Web服务器发送一个请求。如果用户已经登录到你的应用程序,浏览器就会发送该用户的认证Cookie,
|
||||
这样攻击者就可以发送一个正确的请求,以该用户的名义发起一次银行转账。
|
||||
|
||||
To prevent this, your application must make sure that user requests originate in your own
|
||||
To prevent this, your application must ensure that user requests originate in your own
|
||||
application, not on a different site. A common technique is that the server sends a randomly
|
||||
generated authentication token in a cookie, often with the name `XSRF-TOKEN`. Cookies can only
|
||||
be read by the website on which they are set, so only your own application can read this token. On
|
||||
|
@ -387,17 +387,21 @@ h3#xsrf 跨站请求伪造(XSRF)
|
|||
|
||||
Angular applications can customize cookie and header names by binding their own
|
||||
`CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom
|
||||
binding for that type, by adding
|
||||
`provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')})` or
|
||||
`provide(XSRFStrategy, {useClass: MyXSRFStrategy})` to your providers list.
|
||||
binding for that type, by adding either of the following to your providers list:
|
||||
|
||||
Angular应用程序可以通过绑定它们自己的`CookieXSRFStrategy`值来自定义cookie和HTTP头的名字,
|
||||
也可以通过提供一个自定义类型绑定来完全制定`XSRFStrategy`,只要把`provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')})`或`provide(XSRFStrategy, {useClass: MyXSRFStrategy})`加到你的供应商列表里就可以了。
|
||||
也可以通过提供一个自定义类型绑定来完全制定`XSRFStrategy`,
|
||||
只要把下列代码之一加到你的供应商列表里就可以了:
|
||||
|
||||
code-example(language="typescript").
|
||||
{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')}
|
||||
{ provide: XSRFStrategy, useClass: MyXSRFStrategy}
|
||||
|
||||
:marked
|
||||
Learn about Cross Site Request Forgery (XSRF) at the Open Web Application Security Project (OWASP)
|
||||
[here](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) and
|
||||
[here](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet). This [Stanford University
|
||||
paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is a rich source of detail.
|
||||
paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is also a rich source of detail.
|
||||
|
||||
到开放式Web应用程序安全项目(OWASP)的[这里](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29)
|
||||
和[这里](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet)学习更多关于跨站请求伪造(XSRF)的知识。
|
||||
|
|
|
@ -1886,11 +1886,11 @@ block dart-no-truthy-falsey
|
|||
+makeExample('template-syntax/ts/app/app.component.ts', 'trackByHeroes')(format=".")
|
||||
:marked
|
||||
Now set the `NgForTrackBy` directive to that *tracking* function.
|
||||
Angular offers a variety of equivalent syntax choices including these two:
|
||||
|
||||
现在,把`NgForTrackBy`指令设置为那个*追踪*函数。
|
||||
Angular从语法上提供了等价的可选变体,比如这两个:
|
||||
|
||||
+makeExample('template-syntax/ts/app/app.component.html', 'NgForTrackBy-2')(format=".")
|
||||
|
||||
:marked
|
||||
The *tracking* function doesn't eliminate all DOM changes.
|
||||
Angular may have to update the DOM element if the same-hero *properties* have changed.
|
||||
|
|
154
public/news.jade
154
public/news.jade
|
@ -1,11 +1,22 @@
|
|||
.grid-fluid.l-space-bottom-2
|
||||
.c12.text-center
|
||||
h3.text-headline.text-uppercase Core Team
|
||||
|
||||
h3.text-headline.text-uppercase 核心团队
|
||||
.clear
|
||||
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date June 30, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/06/rc4-now-available.html"
|
||||
) RC2 Now Available
|
||||
p Today we’re happy to announce that we are shipping Angular 2.0.0-rc4. This release begins to lay the foundation for improved Angular Compilation, and makes improvements to testing....
|
||||
.author
|
||||
img(src="/resources/images/bios/stephenfluin.jpg")
|
||||
.posted Posted by <b>Stephen Fluin</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date June 21, 2016
|
||||
|
@ -14,115 +25,69 @@
|
|||
target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/06/rc3-now-available.html"
|
||||
) RC3 Now Available
|
||||
p oday we’re happy to announce that we are shipping Angular 2.0.0-rc3. This release includes a fix for a major performance regression in RC2...
|
||||
p Today we’re happy to announce that we are shipping Angular 2.0.0-rc3. This release includes a fix for a major performance regression in RC2...
|
||||
|
||||
.author
|
||||
img(src="/resources/images/bios/stephenfluin.jpg")
|
||||
.posted 发稿人:<b>Stephen Fluin</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date June 15, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/06/rc2-now-available.html"
|
||||
) RC2 Now Available
|
||||
p Today we’re happy to announce that we are shipping Angular 2.0.0-rc2. This release includes 1. Animation Framework 2. Improvements to Forms 3. More than 100 community contributions...
|
||||
.author
|
||||
img(src="/resources/images/bios/stephenfluin.jpg")
|
||||
.posted 发稿人: <b>Stephen Fluin</b>
|
||||
.posted Posted by <b>Stephen Fluin</b>
|
||||
|
||||
.grid-fluid.l-space-bottom-2.l-space-top-4
|
||||
.c12.text-center
|
||||
h3.text-headline.text-uppercase Developer Community
|
||||
|
||||
h3.text-headline.text-uppercase 开发者社区
|
||||
.clear
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date 2016年6月14日
|
||||
.date June 30, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://www.bennadel.com/blog/3116-using-an-item-template-with-an-html-dropdown-menu-component-in-angular-2-rc-3.htm"
|
||||
) Using An Item Template With An HTML Dropdown Menu Component
|
||||
p A while ago, I played around with trying to create an HTML Dropdown menu component in Angular 2. I discovered that you could pass Template references into components for dynamic rendering....
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Ben Nadel</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date June 29, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://channel9.msdn.com/Shows/Office-Dev-Show/Office-Dev-Show-Episode-35-Getting-Started-with-Angular-2-and-the-Microsoft-Graph"
|
||||
) Getting Started with Angular 2 and the Microsoft Graph (Video)
|
||||
p Richard diZerega explores how to connect to the Microsoft Graph from Angular 2 and TypeScript. Richard starts with the Angular 2 quickstart template and gets it connecting to the the Microsoft Graph...
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Richard diZerega</b>
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date June 26, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.mgechev.com/2016/06/26/tree-shaking-angular2-production-build-rollup-javascript/"
|
||||
) Building an Angular 2 Application for Production
|
||||
p During the keynote of ng-conf, the core team managed to drop the size of the “Hello world!” app to less than 50K! In this blog post we’ll explain all the steps we need to go through in order to achieve such results!.
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Minko Gechev</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date June 14, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/06/14/routing-in-angular-2-revisited.html"
|
||||
) Routing in Angular 2 Revisited
|
||||
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/06/14/routing-in-angular-2-revisited.html"
|
||||
) 重新考虑Angular 2的路由
|
||||
|
||||
p Just recently, the Angular team announced yet another version of the new router. Take a first look at the new and better APIs, touching on the most common scenarios...
|
||||
|
||||
p 最近,Angular团队宣布了又一个新路由器版本。抢先看看这些崭新、更好的API,涵盖最常用的场景...
|
||||
.author
|
||||
img(src="/resources/images/bios/pascalprecht.jpg")
|
||||
.posted 发稿人: <b>Pascal Precht</b>
|
||||
.posted Posted by <b>Pascal Precht</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date 2016年6月13日
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://toddmotto.com/rewriting-angular-styleguide-angular-2"
|
||||
) A new Angular 1.x ES2015 styleguide, the path to Angular 2
|
||||
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://toddmotto.com/rewriting-angular-styleguide-angular-2"
|
||||
) 全新Angular 1.x ES2015风格指南,走向Angular 2
|
||||
p Angular 1.x has changed vastly since the original styleguide, and ES6/ES2015 has now become a defacto standard. The new styleguide focuses on using ES2015, offering recommendations on tooling to use it today...
|
||||
|
||||
p 从原始的风格指南到现在,Angular 1.x发生了很大的变化,而且ES6/ES2015也变成了事实上的新标准. 这个新的风格指南的重点是使用了ES2015,提供了环境和工具推荐,让我们可以现在就使用ES20155标准...
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted 发稿人:<b>Todd Motto</b>
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date 2016年6月9日
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://medium.com/@urish/building-simon-with-angular2-iot-fceb78bb18e5"
|
||||
) Building Simon with Angular2-IoT
|
||||
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://medium.com/@urish/building-simon-with-angular2-iot-fceb78bb18e5"
|
||||
) 使用Angular2-IoT建造西蒙游戏
|
||||
|
||||
p I thought it would be fun to see if I could make the same [Angular 2] control/logic code for both a web application and a physical, “Internet of Things” device....
|
||||
|
||||
p 把同样的一套Angular 2的控制器和逻辑代码,应用到一个网络应用程序和一个物理的“物联网”设备....
|
||||
|
||||
.author
|
||||
img(src="/resources/images/bios/angular-gde-bio-placeholder.png")
|
||||
.posted 发稿人:<b>Uri Shaked</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date 2016年6月8日
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/06/08/component-relative-paths-in-angular-2.html"
|
||||
) Component-Relative Paths in Angular 2
|
||||
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/06/08/component-relative-paths-in-angular-2.html"
|
||||
) Angular 2组件的相对路径
|
||||
|
||||
p Component-based development is Angular 2’s most-loved feature. By now you should be familiar with using the @Component decorators to create components.\...
|
||||
|
||||
p Angular 2最受欢迎的特征就是基于组件的开发。到现在,你应该对使用@Component装饰器来创建组件很熟悉\...
|
||||
.author
|
||||
img(src="/resources/images/bios/thomas.jpg")
|
||||
.posted 发稿人:<b>Thomas Burleson</b>
|
||||
|
||||
.grid-fluid.l-space-bottom-2.l-space-top-4
|
||||
.c12.text-center
|
||||
|
@ -131,7 +96,9 @@
|
|||
.grid-fluid
|
||||
.c3
|
||||
p
|
||||
|
||||
.c6
|
||||
|
||||
.article-card
|
||||
.title
|
||||
a(
|
||||
|
@ -139,11 +106,6 @@
|
|||
class="twitter-follow-button"
|
||||
data-show-count="false"
|
||||
) Follow @angularjs
|
||||
a(
|
||||
href="http://twitter.com/angularjs"
|
||||
class="twitter-follow-button"
|
||||
data-show-count="false"
|
||||
) 关注 @angularjs
|
||||
p.
|
||||
<a class="twitter-timeline" data-chrome="nofooter noborders noheader"
|
||||
href="http://twitter.com/angularjs" data-widget-id="700150278465523713"></a>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"index": {
|
||||
"title": "资源库",
|
||||
"subtitle": "Angular 2的资源库"
|
||||
"title": "浏览Angular的资源库"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,126 @@
|
|||
.resources {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
justify-content: center;
|
||||
flex-direction: column-reverse;
|
||||
.h-affix {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.affix-top {
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
.c-resource {
|
||||
h4 {
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.c-resource-nav {
|
||||
margin-top: 48px;
|
||||
width: $unit * 20;
|
||||
z-index: $layer-1;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
color: #373E41;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.category {
|
||||
padding: 10px 0;
|
||||
|
||||
.category-link {
|
||||
display: block;
|
||||
margin: 2px 0;
|
||||
padding: 3px 14px;
|
||||
font-size: 18px !important;
|
||||
|
||||
&:hover {
|
||||
background: #edf0f2;
|
||||
color: #2B85E7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.capitalize {
|
||||
.subcategory {
|
||||
.subcategory-link {
|
||||
display: block;
|
||||
margin: 2px 0;
|
||||
padding: 4px 14px;
|
||||
|
||||
&:hover {
|
||||
background: #edf0f2;
|
||||
color: #2B85E7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.h-anchor-offset {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: -20px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.l-flex--column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.c-resource-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.c-contribute {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.c-resource-header h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.subcategory-title {
|
||||
padding: 16px 23px;
|
||||
margin: 0;
|
||||
background-color: $mist;
|
||||
color: #373E41;
|
||||
}
|
||||
|
||||
.h-capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.h-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.resource-row-link {
|
||||
color: #1a2326;
|
||||
border: transparent solid 1px;
|
||||
margin: 0;
|
||||
padding: 16px 23px 16px 23px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.resource-row-link:hover {
|
||||
text-decoration: none;
|
||||
border-color: #2B85E7;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 8px 8px rgba(1, 67, 163, .24), 0 0 8px rgba(1, 67, 163, .12), 0 6px 18px rgba(43, 133, 231, .12);
|
||||
transform: translate3d(0, -2px, 0);
|
||||
}
|
||||
|
||||
@media(max-width: 900px) {
|
||||
.c-resource-nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,44 @@
|
|||
<div class="resources js-resources-ctrl grid-fixed" ng-controller="ResourcesCtrl as ctrl">
|
||||
<div class="c8">
|
||||
<div class="l-flex--column">
|
||||
<div class="showcase" ng-repeat="(category, categoryObj) in ctrl.fbObject">
|
||||
<header class="c-resource-header">
|
||||
<a class="h-anchor-offset" name="{{category}}"></a>
|
||||
<h2 class="text-headline text-uppercase">{{ category }}</h2>
|
||||
</header>
|
||||
|
||||
<div class="shadow-1">
|
||||
<div ng-repeat="(subCategory, subcategoryObj) in categoryObj">
|
||||
<a class="h-anchor-offset" name="{{subCategory}}"></a>
|
||||
<h3 class="text-uppercase subcategory-title">{{subCategory}}</h3>
|
||||
|
||||
<div ng-repeat="(section, sectionObj) in subcategoryObj">
|
||||
<div ng-repeat="resource in sectionObj">
|
||||
<div class="c-resource" ng-if="resource.rev">
|
||||
<a class="l-flex--column resource-row-link" target="_blank" href="{{resource.url}}">
|
||||
<div>
|
||||
<h4>{{ resource.title }}</h4>
|
||||
<p class="resource-description" ng-if="resource.desc">{{ resource.desc }}</p>
|
||||
<p class="resource-description" ng-if="!resource.desc">No Description</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c3">
|
||||
<div class="c-resource-nav shadow-1 l-flex--column h-affix" ng-class="{ 'affix-top': ctrl.scrollPos > 200 }">
|
||||
<div class="category" ng-repeat="(category, categoryObj) in ctrl.fbObject">
|
||||
<a class="category-link h-capitalize" href="#{{ category }}">{{ category }}</a>
|
||||
<div class="subcategory" ng-repeat="(subCategory, subcategoryObj) in categoryObj">
|
||||
<a class="subcategory-link" href="#{{subCategory}}">{{subCategory}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue