Merge branch 'pr/2' into fix-styleguide-trigger-positioning
This commit is contained in:
commit
0bae5fca69
|
@ -0,0 +1,30 @@
|
|||
module.exports = {
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"beforeEach": true,
|
||||
"it": true,
|
||||
"expect": true
|
||||
},
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"projects": {
|
||||
"live": "angular-io",
|
||||
"ngdocsdev": "ngdocsdev"
|
||||
}
|
||||
}
|
|
@ -21,10 +21,12 @@ _.*
|
|||
**/resources/zips
|
||||
public/docs/xref-*.*
|
||||
_zip-output
|
||||
www
|
||||
www*
|
||||
npm-debug*.log*
|
||||
*.plnkr.html
|
||||
plnkr.html
|
||||
*.eplnkr.html
|
||||
eplnkr.html
|
||||
*plnkr.no-link.html
|
||||
public/docs/*/latest/guide/cheatsheet.json
|
||||
protractor-results.txt
|
||||
|
|
13
.travis.yml
13
.travis.yml
|
@ -2,7 +2,7 @@ dist: trusty
|
|||
sudo: required
|
||||
language: node_js
|
||||
node_js:
|
||||
- "5"
|
||||
- "6"
|
||||
os:
|
||||
- linux
|
||||
env:
|
||||
|
@ -10,20 +10,20 @@ env:
|
|||
- DBUS_SESSION_BUS_ADDRESS=/dev/null
|
||||
- DISPLAY=:99.0
|
||||
- CHROME_BIN=chromium-browser
|
||||
- LATEST_RELEASE=2.0.0-rc.4
|
||||
# using SHA instead of version to fix build-compile issue
|
||||
- LATEST_RELEASE=cfc12c653970c9ad6d807a6a8ebff58edbc568a0
|
||||
- TASK_FLAGS="--dgeni-log=warn"
|
||||
# - TASK_FLAGS=""
|
||||
matrix:
|
||||
- TASK=lint
|
||||
- TASK="run-e2e-tests --fast" SCRIPT=examples-install.sh
|
||||
- TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh
|
||||
- TASK=build-compile SCRIPT=deploy-install.sh WAIT="travis_wait 50"
|
||||
- TASK=build-compile SCRIPT=deploy-install-preview.sh WAIT="travis_wait 50"
|
||||
- TASK=build-compile SCRIPT=deploy-install.sh WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
|
||||
- TASK=build-compile SCRIPT=deploy-install-preview.sh WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh
|
||||
- env: TASK=build-compile SCRIPT=deploy-install-preview.sh WAIT="travis_wait 50"
|
||||
- env: TASK=build-compile SCRIPT=deploy-install-preview.sh WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
|
||||
before_install:
|
||||
- source ./scripts/env-set.sh
|
||||
- ./scripts/before-install.sh
|
||||
|
@ -34,3 +34,4 @@ before_script:
|
|||
- sh -e /etc/init.d/xvfb start
|
||||
script:
|
||||
- $WAIT gulp $TASK $TASK_FLAGS
|
||||
- if [[ -n "$POST_SCRIPT" ]]; then ./scripts/$POST_SCRIPT; fi
|
||||
|
|
14
README.md
14
README.md
|
@ -1,7 +1,7 @@
|
|||
# Angular.io
|
||||
[![Build Status][travis-badge]][travis-badge-url]
|
||||
|
||||
Angular.io is site for Angular 2 **documentation** .
|
||||
Angular.io is site for Angular **documentation** .
|
||||
|
||||
This site also includes links to other helpful angular resources including
|
||||
Angular 2, Angular 1, Angular Material, and AngularFire.
|
||||
|
@ -21,16 +21,18 @@ Filing issues is helpful but **pull requests** that improve the docs are even be
|
|||
|
||||
Learn how to [contribute to Angular.io](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
|
||||
|
||||
> **IMPORTANT**: Do _NOT_ make changes to cached files under `public/docs/ts/_cache`. Cached files are updated through a separate workflow.
|
||||
|
||||
## Development Setup
|
||||
This site relies heavily on node and npm.
|
||||
|
||||
1. Make sure you are using at least node v.5+ and latest npm;
|
||||
if not install [nvm](https://github.com/creationix/nvm) to get node going on your machine.
|
||||
|
||||
1. install these npm packages *globally*: `npm install -g harp gulp`
|
||||
1. Install these npm packages *globally*: `npm install -g harp gulp`
|
||||
|
||||
1. clone this repo and the [angular source code repo](https://github.com/angular/angular) to the same parent directory.
|
||||
The two cloned repo directories must be sibling.
|
||||
1. Clone this repo, the [angular/angular source code repo](https://github.com/angular/angular), and the [dart-lang/angular2 source code repo](https://github.com/dart-lang/angular2) to the same parent directory.
|
||||
The three cloned repo directories must be siblings, with the latter two repo directories named **angular** and **angular-dart**, respectively.
|
||||
|
||||
1. cd into root directory `angular.io/`
|
||||
|
||||
|
@ -71,11 +73,11 @@ If you are only going to work on a specific part of the docs, such as the dev gu
|
|||
## Code Sample Development
|
||||
|
||||
All documentation is supported by sample code and plunkers.
|
||||
Such code resides in the `public/docs/_examples` directory, under chapter-specific directories, further divided by language track.
|
||||
Such code resides in the `public/docs/_examples` directory, under page-specific directories, further divided by language track.
|
||||
|
||||
For example, the TypeScript QuickStart sample is in `public/docs/_examples/quickstart/ts`.
|
||||
|
||||
All samples are in a consistent directory structure using the same styles and the same npm packages, including the latest release of Angular 2.
|
||||
All samples are in a consistent directory structure using the same styles and the same npm packages, including the latest release of Angular.
|
||||
This consistency is possible in part thanks to gulp-driven tooling.
|
||||
To run the samples locally and confirm that they work properly,
|
||||
take the following extra steps to prepare the environment:
|
||||
|
|
219
gulpfile.js
219
gulpfile.js
|
@ -45,7 +45,8 @@ var STYLES_SOURCE_PATH = path.join(TOOLS_PATH, 'styles-builder/less');
|
|||
|
||||
var docShredder = require(path.resolve(TOOLS_PATH, 'doc-shredder/doc-shredder'));
|
||||
var exampleZipper = require(path.resolve(TOOLS_PATH, '_example-zipper/exampleZipper'));
|
||||
var plunkerBuilder = require(path.resolve(TOOLS_PATH, 'plunker-builder/plunkerBuilder'));
|
||||
var regularPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/regularPlunker'));
|
||||
var embeddedPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/embeddedPlunker'));
|
||||
var fsUtils = require(path.resolve(TOOLS_PATH, 'fs-utils/fsUtils'));
|
||||
|
||||
const isSilent = !!argv.silent;
|
||||
|
@ -72,9 +73,10 @@ var _apiShredOptions = {
|
|||
logLevel: _dgeniLogLevel
|
||||
};
|
||||
|
||||
const relDartDocApiDir = path.join('doc', 'api');
|
||||
var _apiShredOptionsForDart = {
|
||||
lang: 'dart',
|
||||
examplesDir: path.resolve(ngPathFor('dart'), 'examples'),
|
||||
examplesDir: path.resolve(ngPathFor('dart'), 'example'),
|
||||
fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
|
||||
zipDir: path.join(RESOURCES_PATH, 'zips/api'),
|
||||
logLevel: _dgeniLogLevel
|
||||
|
@ -89,15 +91,12 @@ var _excludeMatchers = _excludePatterns.map(function(excludePattern){
|
|||
var _exampleBoilerplateFiles = [
|
||||
'.editorconfig',
|
||||
'a2docs.css',
|
||||
'karma.conf.js',
|
||||
'karma-test-shim.js',
|
||||
'package.json',
|
||||
'styles.css',
|
||||
'systemjs.config.js',
|
||||
'tsconfig.json',
|
||||
'tslint.json',
|
||||
'typings.json',
|
||||
'wallaby.js'
|
||||
'typings.json'
|
||||
];
|
||||
|
||||
var _exampleDartWebBoilerPlateFiles = ['a2docs.css', 'styles.css'];
|
||||
|
@ -118,22 +117,35 @@ var _styleLessName = 'a2docs.less';
|
|||
// or a regex pattern to match any one of 'ts', 'js', or 'dart'.
|
||||
// Default: 'ts|js' except for the "full site build" tasks (see below),
|
||||
// for which it is 'all'.
|
||||
//
|
||||
var lang, langs, buildDartApiDocs = false;
|
||||
|
||||
// langs and skipLangs partition ['ts', 'js', 'dart'].
|
||||
var lang, langs, skipLangs, buildDartApiDocs = false;
|
||||
function configLangs(langOption) {
|
||||
const fullSiteBuildTasks = ['build-compile', 'check-serve', 'check-deploy'];
|
||||
const fullSiteBuildTasks = ['build-compile', 'check-deploy', 'harp-compile'];
|
||||
const buildAllDocs = argv['_'] &&
|
||||
fullSiteBuildTasks.some((task) => argv['_'].indexOf(task) >= 0);
|
||||
const langDefault = buildAllDocs ? 'all' : 'ts|js';
|
||||
lang = (langOption || langDefault).toLowerCase();
|
||||
if (lang === 'all') lang = 'ts|js|dart';
|
||||
langs = lang.match(/\w+/g); // the languages in `lang` as an array
|
||||
gutil.log('Building docs for: ' + lang);
|
||||
if (langOption === '') {
|
||||
lang = '';
|
||||
langs = [];
|
||||
} else {
|
||||
lang = (langOption || langDefault).toLowerCase();
|
||||
if (lang === 'all') lang = 'ts|js|dart';
|
||||
langs = lang.match(/\w+/g); // the languages in `lang` as an array
|
||||
}
|
||||
gutil.log(`Building docs for: [${langs}]`);
|
||||
if (langs.indexOf('dart') >= 0) {
|
||||
buildDartApiDocs = true;
|
||||
// For Dart, be proactive about checking for the repo
|
||||
checkAngularProjectPath(ngPathFor('dart'));
|
||||
} else {
|
||||
argv.pub = false;
|
||||
}
|
||||
skipLangs = [];
|
||||
['ts', 'js', 'dart'].forEach(lang => {
|
||||
if (langs.indexOf(lang) < 0) skipLangs.push(lang);
|
||||
});
|
||||
gutil.log(`Skipped languages: [${skipLangs}]`);
|
||||
}
|
||||
configLangs(argv.lang);
|
||||
|
||||
|
@ -344,10 +356,15 @@ function runE2eDartTests(appDir, outputFile) {
|
|||
gutil.log('http-server failed to launch over ' + deployDir);
|
||||
return false;
|
||||
}
|
||||
var pubUpgradeSpawnInfo = spawnExt('pub', ['upgrade'], { cwd: appDir });
|
||||
var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) {
|
||||
return spawnExt('pub', ['build'], { cwd: appDir }).promise;
|
||||
});
|
||||
if (argv.pub === false) {
|
||||
var prepPromise = Promise.resolve(true);
|
||||
gutil.log('Skipping pub upgrade and pub build (--no-pub flag present)');
|
||||
} else {
|
||||
var pubUpgradeSpawnInfo = spawnExt('pub', ['upgrade'], { cwd: appDir });
|
||||
var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) {
|
||||
return spawnExt('pub', ['build'], { cwd: appDir }).promise;
|
||||
});
|
||||
}
|
||||
return runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile);
|
||||
}
|
||||
|
||||
|
@ -441,10 +458,10 @@ gulp.task('add-example-boilerplate', function(done) {
|
|||
// copies boilerplate files to locations
|
||||
// where an example app is found
|
||||
gulp.task('_copy-example-boilerplate', function (done) {
|
||||
if (!argv.fast) buildStyles(copyExampleBoilerplate, done);
|
||||
return argv.fast ? done() : buildStyles(copyExampleBoilerplate, done);
|
||||
});
|
||||
|
||||
//Builds Angular 2 Docs CSS file from Bootstrap npm LESS source
|
||||
//Builds Angular Docs CSS file from Bootstrap npm LESS source
|
||||
//and copies the result to the _examples folder to be included as
|
||||
//part of the example boilerplate.
|
||||
function buildStyles(cb, done){
|
||||
|
@ -504,23 +521,42 @@ gulp.task('remove-example-boilerplate', function() {
|
|||
// either release or current build packages
|
||||
// Examples:
|
||||
// gulp install-example-angular --build // use current build packages
|
||||
// gulp install-example-angular --build=2.0.0-b43f954 // use tagged packages
|
||||
// gulp install-example-angular // restore release packages
|
||||
//
|
||||
// Find the tags here: https://github.com/angular/core-builds/releases
|
||||
gulp.task('install-example-angular', installExampleAngular);
|
||||
|
||||
function installExampleAngular() {
|
||||
var sources;
|
||||
var template;
|
||||
var libs = [
|
||||
'core', 'common', 'compiler',
|
||||
'core', 'common', 'compiler', 'compiler-cli',
|
||||
'platform-browser', 'platform-browser-dynamic',
|
||||
'forms', 'http', 'router', 'upgrade'];
|
||||
|
||||
var build = argv.build;
|
||||
if (build) {
|
||||
if (typeof build === 'string') {
|
||||
build = (build[0]==='#' ? '' : '#') + build;
|
||||
} else {
|
||||
build = '';
|
||||
}
|
||||
} else{
|
||||
build = 'npm';
|
||||
}
|
||||
// Like: "angular/core-builds" or "@angular/core"
|
||||
sources = libs.map( lib => argv.build ? `angular/${lib}-builds` : `@angular/${lib}`);
|
||||
sources = libs.map( lib => {
|
||||
return build === 'npm'
|
||||
? `@angular/${lib}`
|
||||
: `git+https://github.com/angular/${lib}-builds${build}`;
|
||||
});
|
||||
|
||||
if (argv.build) { sources.push('@angular/tsc-wrapped');} // tsc-wrapped needed for builds
|
||||
|
||||
sources.push('@angular/router-deprecated');
|
||||
|
||||
gutil.log(`Installing Angular npm packages from ${argv.build ? 'BUILD' : 'RELEASE'}`);
|
||||
gutil.log(`Installing Angular packages from ${build === 'npm' ? 'NPM' : 'BUILD ' + build}`);
|
||||
|
||||
var spawnInfo = spawnExt('rm', ['-rf', 'node_modules/@angular'], { cwd: EXAMPLES_PATH});
|
||||
return spawnInfo.promise
|
||||
|
@ -594,8 +630,10 @@ gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function()
|
|||
return buildApiDocsForDart();
|
||||
});
|
||||
|
||||
// Using the --build flag will use systemjs.config.plunker.build.js (for preview builds)
|
||||
gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() {
|
||||
return plunkerBuilder.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build });
|
||||
regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build });
|
||||
return embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build, targetSelf: argv.targetSelf });
|
||||
});
|
||||
|
||||
gulp.task('build-dart-cheatsheet', [], function() {
|
||||
|
@ -604,20 +642,20 @@ gulp.task('build-dart-cheatsheet', [], function() {
|
|||
|
||||
gulp.task('dartdoc', ['pub upgrade'], function() {
|
||||
const ngRepoPath = ngPathFor('dart');
|
||||
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, 'doc'))) {
|
||||
gutil.log('Skipping dartdoc: --fast flag enabled and "doc" dir exists');
|
||||
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, relDartDocApiDir))) {
|
||||
gutil.log(`Skipping dartdoc: --fast flag enabled and api dir exists (${relDartDocApiDir})`);
|
||||
return true;
|
||||
}
|
||||
checkAngularProjectPath(ngRepoPath);
|
||||
const topLevelLibFilePath = path.resolve(ngRepoPath, 'lib', 'angular2.dart');
|
||||
const tmpPath = topLevelLibFilePath + '.disabled';
|
||||
if (!fs.existsSync(topLevelLibFilePath)) throw new Error(`Missing file: ${topLevelLibFilePath}`);
|
||||
fs.renameSync(topLevelLibFilePath, tmpPath);
|
||||
renameIfExistsSync(topLevelLibFilePath, tmpPath);
|
||||
gutil.log(`Hiding top-level angular2 library: ${topLevelLibFilePath}`);
|
||||
const dartdoc = spawnExt('dartdoc', ['--output', 'doc/api', '--add-crossdart'], { cwd: ngRepoPath});
|
||||
// Remove dartdoc '--add-crossdart' flag while we are fixing links to API pages.
|
||||
const dartdoc = spawnExt('dartdoc', ['--output', relDartDocApiDir], { cwd: ngRepoPath});
|
||||
return dartdoc.promise.finally(() => {
|
||||
gutil.log(`Restoring top-level angular2 library: ${topLevelLibFilePath}`);
|
||||
fs.renameSync(tmpPath, topLevelLibFilePath);
|
||||
renameIfExistsSync(tmpPath, topLevelLibFilePath);
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -678,12 +716,12 @@ gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){
|
|||
});
|
||||
});
|
||||
|
||||
gulp.task('harp-compile', [], function() {
|
||||
gulp.task('harp-compile', () => {
|
||||
return harpCompile()
|
||||
});
|
||||
|
||||
gulp.task('serve', [], function() {
|
||||
// Harp will serve files from workspace.
|
||||
gulp.task('harp-serve', () => {
|
||||
// Harp will watch and serve workspace files.
|
||||
const cmd = 'npm run harp -- server .';
|
||||
gutil.log('Launching harp server (over project files)');
|
||||
gutil.log(` > ${cmd}`);
|
||||
|
@ -691,7 +729,7 @@ gulp.task('serve', [], function() {
|
|||
return execPromise(cmd);
|
||||
});
|
||||
|
||||
gulp.task('serve-www', [], function() {
|
||||
gulp.task('serve-www', () => {
|
||||
// Serve generated site.
|
||||
return execPromise('npm run live-server ./www');
|
||||
});
|
||||
|
@ -700,13 +738,6 @@ gulp.task('build-compile', ['build-docs'], function() {
|
|||
return harpCompile();
|
||||
});
|
||||
|
||||
gulp.task('check-serve', ['build-docs'], function() {
|
||||
return harpCompile().then(function() {
|
||||
gutil.log('Launching live-server over ./www');
|
||||
return execPromise('npm run live-server ./www');
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('check-deploy', ['build-docs'], function() {
|
||||
return harpCompile().then(function() {
|
||||
gutil.log('compile ok');
|
||||
|
@ -802,7 +833,7 @@ gulp.task('_shred-clean-devguide', function(cb) {
|
|||
gulp.task('_shred-api-examples', ['_shred-clean-api'], function() {
|
||||
const promises = [];
|
||||
gutil.log('Shredding API examples for languages: ' + langs.join(', '));
|
||||
langs.forEach((lang) => {
|
||||
langs.forEach(lang => {
|
||||
if (lang === 'js') return; // JS is handled via TS.
|
||||
checkAngularProjectPath(ngPathFor(lang));
|
||||
const options = lang == 'dart' ? _apiShredOptionsForDart : _apiShredOptions;
|
||||
|
@ -852,26 +883,40 @@ gulp.task('lint', function() {
|
|||
function harpCompile() {
|
||||
// Supposedly running in production makes harp faster
|
||||
// and less likely to drown in node_modules.
|
||||
env({
|
||||
vars: { NODE_ENV: "production" }
|
||||
});
|
||||
env({ vars: { NODE_ENV: "production" } });
|
||||
gutil.log("NODE_ENV: " + process.env.NODE_ENV);
|
||||
|
||||
if(skipLangs && fs.existsSync('www') && backupApiHtmlFilesExist('www')) {
|
||||
gutil.log(`Harp site recompile: skipping recompilation of API docs for [${skipLangs}]`);
|
||||
gutil.log(`API docs will be copied from existing www folder.`)
|
||||
del.sync('www-backup'); // remove existing backup if it exists
|
||||
renameIfExistsSync('www', 'www-backup');
|
||||
} else {
|
||||
gutil.log(`Harp full site compile, including API docs for all languages.`);
|
||||
if (skipLangs)
|
||||
gutil.log(`Ignoring API docs skip set (${skipLangs}) because full ` +
|
||||
`site has not been built yet or some API HTML files are missing.`);
|
||||
}
|
||||
|
||||
var deferred = Q.defer();
|
||||
gutil.log('running harp compile...');
|
||||
showHideExampleNodeModules('hide');
|
||||
showHideApiDir('hide');
|
||||
var spawnInfo = spawnExt('npm',['run','harp', '--', 'compile', '.', './www' ]);
|
||||
spawnInfo.promise.then(function(x) {
|
||||
gutil.log("NODE_ENV: " + process.env.NODE_ENV);
|
||||
showHideExampleNodeModules('show');
|
||||
showHideApiDir('show');
|
||||
if (x !== 0) {
|
||||
deferred.reject(x)
|
||||
} else {
|
||||
restoreApiHtml();
|
||||
deferred.resolve(x);
|
||||
}
|
||||
}).catch(function(e) {
|
||||
gutil.log("NODE_ENV: " + process.env.NODE_ENV);
|
||||
showHideExampleNodeModules('show');
|
||||
showHideApiDir('show');
|
||||
deferred.reject(e);
|
||||
});
|
||||
return deferred.promise;
|
||||
|
@ -970,6 +1015,54 @@ function showHideExampleNodeModules(showOrHide) {
|
|||
}
|
||||
}
|
||||
|
||||
// Show/hide the API docs harp source folder for every lang in skipLangs.
|
||||
function showHideApiDir(showOrHide) {
|
||||
skipLangs.forEach(lang => {
|
||||
_showHideApiDir(lang, showOrHide);
|
||||
});
|
||||
}
|
||||
|
||||
// Rename the API docs harp source folder for lang to/from 'api' to '_api-tmp-foo'.
|
||||
function _showHideApiDir(lang, showOrHide) {
|
||||
const vers = 'latest';
|
||||
const basePath = path.join(DOCS_PATH, lang, vers);
|
||||
const apiDirPath = path.join(basePath, 'api');
|
||||
const disabledApiDirPath = path.join(basePath, '_api-tmp-hide-from-jade');
|
||||
const args = showOrHide == 'hide'
|
||||
? [apiDirPath, disabledApiDirPath]
|
||||
: [disabledApiDirPath, apiDirPath];
|
||||
renameIfExistsSync(...args);
|
||||
}
|
||||
|
||||
// For each lang in skipLangs, copy the API dir from www-backup to www.
|
||||
function restoreApiHtml() {
|
||||
const vers = 'latest';
|
||||
skipLangs.forEach(lang => {
|
||||
const relApiDir = path.join('docs', lang, vers, 'api');
|
||||
const wwwApiSubdir = path.join('www', relApiDir);
|
||||
const backupApiSubdir = path.join('www-backup', relApiDir);
|
||||
if (fs.existsSync(backupApiSubdir) || argv.forceSkipApi !== true) {
|
||||
gutil.log(`cp ${backupApiSubdir} ${wwwApiSubdir}`)
|
||||
fs.copySync(backupApiSubdir, wwwApiSubdir);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// For each lang in skipLangs, ensure API dir exists in www-backup
|
||||
function backupApiHtmlFilesExist(folderName) {
|
||||
const vers = 'latest';
|
||||
var result = 1;
|
||||
skipLangs.forEach(lang => {
|
||||
const relApiDir = path.join('docs', lang, vers, 'api');
|
||||
const backupApiSubdir = path.join(folderName, relApiDir);
|
||||
if (!fs.existsSync(backupApiSubdir)) {
|
||||
gutil.log(`WARNING: API docs HTML folder doesn't exist: ${backupApiSubdir}`);
|
||||
result = 0;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Copies fileNames into destPaths, setting the mode of the
|
||||
// files at the destination as optional_destFileMode if given.
|
||||
// returns a promise
|
||||
|
@ -1069,11 +1162,16 @@ function watchAndSync(options, cb) {
|
|||
var browserSync = require('browser-sync').create();
|
||||
browserSync.init({proxy: 'localhost:9000'});
|
||||
|
||||
// When using the --focus=name flag, only **/name/**/*.* example files and
|
||||
// **/name.jade files are watched. This is useful for performance reasons.
|
||||
// Example: gulp serve-and-sync --focus=architecture
|
||||
var focus = argv.focus;
|
||||
|
||||
if (options.devGuide) {
|
||||
devGuideExamplesWatch(_devguideShredOptions, browserSync.reload);
|
||||
devGuideExamplesWatch(_devguideShredOptions, browserSync.reload, focus);
|
||||
}
|
||||
if (options.devGuideJade) {
|
||||
devGuideSharedJadeWatch( { jadeDir: DOCS_PATH}, browserSync.reload);
|
||||
devGuideSharedJadeWatch( { jadeDir: DOCS_PATH}, browserSync.reload, focus);
|
||||
}
|
||||
if (options.apiDocs) {
|
||||
apiSourceWatch(browserSync.reload);
|
||||
|
@ -1088,11 +1186,10 @@ function watchAndSync(options, cb) {
|
|||
|
||||
// returns a promise;
|
||||
function askDeploy() {
|
||||
|
||||
prompt.start();
|
||||
var schema = {
|
||||
name: 'shouldDeploy',
|
||||
description: 'Deploy to Firebase? (y/n): ',
|
||||
description: 'Deploy to Firebase? (y/n)',
|
||||
type: 'string',
|
||||
pattern: /Y|N|y|n/,
|
||||
message: "Respond with either a 'y' or 'n'",
|
||||
|
@ -1114,7 +1211,7 @@ function filterOutExcludedPatterns(fileNames, excludeMatchers) {
|
|||
}
|
||||
|
||||
function apiSourceWatch(postBuildAction) {
|
||||
var srcPattern = [path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/src/**/*.*')];
|
||||
var srcPattern = [path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/**/*.*')];
|
||||
gulp.watch(srcPattern, {readDelay: 500}, function (event, done) {
|
||||
gutil.log('API source changed');
|
||||
gutil.log('Event type: ' + event.event); // added, changed, or deleted
|
||||
|
@ -1141,8 +1238,9 @@ function apiExamplesWatch(postShredAction) {
|
|||
});
|
||||
}
|
||||
|
||||
function devGuideExamplesWatch(shredOptions, postShredAction) {
|
||||
var includePattern = path.join(shredOptions.examplesDir, '**/*.*');
|
||||
function devGuideExamplesWatch(shredOptions, postShredAction, focus) {
|
||||
var watchPattern = focus ? '**/' + focus + '/**/*.*' : '**/*.*';
|
||||
var includePattern = path.join(shredOptions.examplesDir, watchPattern);
|
||||
// removed this version because gulp.watch has the same glob issue that dgeni has.
|
||||
// var excludePattern = '!' + path.join(shredOptions.examplesDir, '**/node_modules/**/*.*');
|
||||
// gulp.watch([includePattern, excludePattern], {readDelay: 500}, function (event, done) {
|
||||
|
@ -1158,8 +1256,9 @@ function devGuideExamplesWatch(shredOptions, postShredAction) {
|
|||
});
|
||||
}
|
||||
|
||||
function devGuideSharedJadeWatch(shredOptions, postShredAction) {
|
||||
var includePattern = path.join(DOCS_PATH, '**/*.jade');
|
||||
function devGuideSharedJadeWatch(shredOptions, postShredAction, focus) {
|
||||
var watchPattern = focus ? '**/' + focus + '.jade' : '**/*.jade';
|
||||
var includePattern = path.join(DOCS_PATH, watchPattern);
|
||||
// removed this version because gulp.watch has the same glob issue that dgeni has.
|
||||
// var excludePattern = '!' + path.join(shredOptions.jadeDir, '**/node_modules/**/*.*');
|
||||
// gulp.watch([includePattern, excludePattern], {readDelay: 500}, function (event, done) {
|
||||
|
@ -1235,15 +1334,14 @@ function buildDartCheatsheet() {
|
|||
|
||||
|
||||
function buildApiDocsForDart() {
|
||||
const apiDir = 'api';
|
||||
const vers = 'latest';
|
||||
const dab = require('./tools/dart-api-builder/dab')(ANGULAR_IO_PROJECT_PATH);
|
||||
const log = dab.log;
|
||||
|
||||
log.level = _dgeniLogLevel;
|
||||
const dabInfo = dab.dartPkgConfigInfo;
|
||||
dabInfo.ngIoDartApiDocPath = path.join(DOCS_PATH, 'dart', vers, apiDir);
|
||||
dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), 'doc', apiDir);
|
||||
dabInfo.ngIoDartApiDocPath = path.join(DOCS_PATH, 'dart', vers, 'api');
|
||||
dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), relDartDocApiDir);
|
||||
// Exclude API entries for developer/internal libraries. Also exclude entries for
|
||||
// the top-level catch all "angular2" library (otherwise every entry appears twice).
|
||||
dabInfo.excludeLibRegExp = new RegExp(/^(?!angular2)|\.testing|_|codegen|^angular2$/);
|
||||
|
@ -1455,3 +1553,12 @@ function checkAngularProjectPath(_ngPath) {
|
|||
if (fs.existsSync(ngPath)) return;
|
||||
throw new Error('API related tasks require the angular2 repo to be at ' + ngPath);
|
||||
}
|
||||
|
||||
function renameIfExistsSync(oldPath, newPath) {
|
||||
if (fs.existsSync(oldPath)) {
|
||||
gutil.log(`Rename: mv ${oldPath} ${newPath}`);
|
||||
fs.renameSync(oldPath, newPath);
|
||||
} else {
|
||||
gutil.log(`renameIfExistsSync cannot rename, path not found: ${oldPath}`);
|
||||
}
|
||||
}
|
||||
|
|
52
harp.json
52
harp.json
|
@ -85,7 +85,7 @@
|
|||
"picture": "/resources/images/bios/tobias.jpg",
|
||||
"twitter": "tbosch1009",
|
||||
"website": "https://plus.google.com/+TobiasBosch",
|
||||
"bio": "Tobias Bosch is a software engineer at Google. He is part of the Angular core team and works on Angular 2.",
|
||||
"bio": "Tobias Bosch is a software engineer at Google. He is part of the Angular core team and works on Angular.",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
|
@ -184,7 +184,7 @@
|
|||
"picture": "/resources/images/bios/hansl.jpg",
|
||||
"twitter": "hanslatwork",
|
||||
"website": "http://www.codingatwork.com/",
|
||||
"bio": "Hans is a software engineer at Google on the Angular team and was previously at Slack. He works everyday to help make it easier for everyone to create beautiful, consistent web applications using Angular2, using Material Design components and the CLI tool.",
|
||||
"bio": "Hans is a software engineer at Google on the Angular team and was previously at Slack. He works everyday to help make it easier for everyone to create beautiful, consistent web applications using Angular, using Material Design components and the CLI tool.",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
|
@ -316,6 +316,12 @@
|
|||
"bio": "Max Sills is Angular's Open Source lawyer.",
|
||||
"type": "Google"
|
||||
},
|
||||
"shannon": {
|
||||
"name": "Shannon Ayres",
|
||||
"picture": "/resources/images/bios/shannon.jpg",
|
||||
"bio": "Shannon is a technical editor in Developer Relations at Google. She loves movies, especially Sunset Boulevard, and her favorite TV show is The Walking Dead. Her mission: Righting wrong writing!",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
"pawel": {
|
||||
"name": "Pawel Kozlowski",
|
||||
|
@ -354,7 +360,7 @@
|
|||
"picture": "/resources/images/bios/marclaval.jpg",
|
||||
"twitter": "marclaval",
|
||||
"website": "https://github.com/mlaval",
|
||||
"bio": "Marc is a manager at Amadeus where he leads the team in charge of developing and recommending UI frameworks for the company. He is also an open source developer and a contributor to Angular 2.",
|
||||
"bio": "Marc is a manager at Amadeus where he leads the team in charge of developing and recommending UI frameworks for the company. He is also an open source developer and a contributor to Angular.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
|
@ -372,7 +378,7 @@
|
|||
"picture": "/resources/images/bios/patrick-stapleton.jpg",
|
||||
"twitter": "gdi2290",
|
||||
"website": "https://angularclass.com",
|
||||
"bio": "Also know as PatrickJS where JS stands for his middle and last names. Patrick is very active in Open-Source with over 4,300+ contributions in the last year alone on projects such as Angular2, AngularJS, FalcorJS, Docker, Bootstrap, gulp, and redis to name a few. He is also working on the development of Angular 2 server-side rendering as Universal Angular 2 and teaching Modern Web Development at AngularClass. He was previously the CTO of Keychain Logistics, a HackReactor Instructor and Alum.",
|
||||
"bio": "Also know as PatrickJS where JS stands for his middle and last names. Patrick is very active in Open-Source with over 4,300+ contributions in the last year alone on projects such as Angular2, AngularJS, FalcorJS, Docker, Bootstrap, gulp, and redis to name a few. He is also working on the development of Angular server-side rendering as Universal Angular and teaching Modern Web Development at AngularClass. He was previously the CTO of Keychain Logistics, a HackReactor Instructor and Alum.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
|
@ -391,6 +397,7 @@
|
|||
"bio": "Ward is an all-around developer with JavaScript, node, and .net chops. He's a frequent conference speaker and podcaster, trainer, Google Developer Expert for Angular, Microsoft MVP, and PluralSight author. He is also president of IdeaBlade, an enterprise software consulting firm and the makers of breeze.js. He would like to get more sleep and spend more time in the mountains.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"johnpapa": {
|
||||
"name": "John Papa",
|
||||
"picture": "/resources/images/bios/john-papa.jpg",
|
||||
|
@ -399,6 +406,7 @@
|
|||
"bio": "John is a Google Developer Expert, Microsoft Regional Director and MVP, frequent author of courses for Pluralsight, a former technology Evangelist for Microsoft front end teams, and author of the popular Angular Style Guide. He can often be found speaking around the world at keynotes and sessions for many conferences. You can always find John at johnpapa.net or on twitter at @john_papa.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"martinstaffa": {
|
||||
"name": "Martin Staffa",
|
||||
"picture": "/resources/images/bios/martinstaffa.jpg",
|
||||
|
@ -406,6 +414,7 @@
|
|||
"bio": "Martin is an English major turned web developer who loves frontend stuff. He's been part of the Angular 1 team since 2014. If you can't find him roaming the Github issue queues, he's probably out with his camera somewhere.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"topherfangio": {
|
||||
"name": "Topher Fangio",
|
||||
"picture": "/resources/images/bios/topherfangio.jpg",
|
||||
|
@ -414,6 +423,7 @@
|
|||
"bio": "Topher loves the web and how it empowers new forms of creativity, connection and business. He is currently a core contributor on the Angular Material project and sometimes blogs about random things.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"filipesilva": {
|
||||
"name": "Filipe Silva",
|
||||
"picture": "/resources/images/bios/filipe-silva.jpg",
|
||||
|
@ -422,6 +432,7 @@
|
|||
"bio": "Filipe is a passion-driven developer that always strives for the most elegant solution for each problem. He is currently an author for Angular.io, a core contributor for Angular-CLI and senior front end engineer at KonnectAgain. When not busy going through PRs, you can find him scouring reddit for new dinner recipes to cook or enjoying a craft beer in Dublin.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"teropa": {
|
||||
"name": "Tero Parviainen",
|
||||
"picture": "/resources/images/bios/teropa.jpg",
|
||||
|
@ -430,15 +441,17 @@
|
|||
"bio": "Tero is an independent software developer and writer. He's been building web applications for his whole professional career, and has almost figured out how to do vertical centering in CSS.",
|
||||
"type": "Community"
|
||||
},
|
||||
"deborah": {
|
||||
|
||||
"deborah": {
|
||||
"name": "Deborah Kurata",
|
||||
"picture": "/resources/images/bios/deborah.jpg",
|
||||
"twitter": "deborahkurata",
|
||||
"website": "http://blogs.msmvps.com/deborahk/",
|
||||
"bio": "Deborah is an independent software developer and author. She is author of several Pluralsight courses including: 'Angular 2: Getting Started'",
|
||||
"type": "Community"
|
||||
},
|
||||
"jesusrodriguez": {
|
||||
},
|
||||
|
||||
"jesusrodriguez": {
|
||||
"name": "Jesús Rodríguez",
|
||||
"picture": "/resources/images/bios/jesus-rodriguez.jpg",
|
||||
"twitter": "foxandxss",
|
||||
|
@ -446,6 +459,7 @@
|
|||
"bio": "Jesus is an open source lover, a book author and editor, and AngularUI lead developer. He is currently a core contributor to the UI Bootstrap project.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"torgeirhelgevold": {
|
||||
"name": "Torgeir Helgevold",
|
||||
"picture": "/resources/images/bios/torgeirhelgevold.jpg",
|
||||
|
@ -454,6 +468,7 @@
|
|||
"bio": "Torgeir (Tor) is a front-end architect with a passion for JavaScript development. He is also an author for angular.io and an active tech blogger.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"fatimaremtullah": {
|
||||
"name": "Fatima Remtullah",
|
||||
"picture": "/resources/images/bios/fatima.jpg",
|
||||
|
@ -462,6 +477,7 @@
|
|||
"bio": "Fatima is a Product Designer and Front-End Developer. When she is not nerding out she is probably eating an abundance of cookies.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"eric": {
|
||||
"name": "Eric Jimenez",
|
||||
"picture": "/resources/images/bios/eric.jpg",
|
||||
|
@ -470,6 +486,7 @@
|
|||
"bio": "Eric is a gamer, writer, and programmer.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"mikeryan": {
|
||||
"name": "Mike Ryan",
|
||||
"picture": "/resources/images/bios/mikeryan.jpg",
|
||||
|
@ -477,7 +494,8 @@
|
|||
"website": "https://medium.com/@MikeRyan52",
|
||||
"bio": "Mike Ryan is a Software Engineer at Synapse Wireless, working on solving challenging problems in the internet-of-things space. He is an advocate of reactive programming and a core contributor to the ngrx project.",
|
||||
"type": "Community"
|
||||
},
|
||||
},
|
||||
|
||||
"rex": {
|
||||
"name": "Rex Ye",
|
||||
"picture": "/resources/images/bios/rex.jpg",
|
||||
|
@ -485,6 +503,7 @@
|
|||
"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",
|
||||
|
@ -492,6 +511,7 @@
|
|||
"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"
|
||||
},
|
||||
|
||||
"brandonroberts": {
|
||||
"name": "Brandon Roberts",
|
||||
"picture": "/resources/images/bios/brandonroberts.jpg",
|
||||
|
@ -500,12 +520,28 @@
|
|||
"bio": "Brandon is a front-end developer for a game studio developing web applications for STEM-based learning games. He is also a natural born troubleshooter who helps solve Angular issues on Github and Gitter support channels, particularly dealing with routing. He is also a member of the Angular docs team.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"crisbeto": {
|
||||
"name": "Kristiyan Kostadinov",
|
||||
"picture": "/resources/images/bios/crisbeto.jpg",
|
||||
"website": "http://crisbeto.com/",
|
||||
"bio": "Kristiyan is a front-end developer, passionate open-source contributor and a core team member on Angular Material.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
"gkalpak": {
|
||||
"name": "Georgios Kalpakas",
|
||||
"picture": "/resources/images/bios/gkalpak.jpg",
|
||||
"website": "https://github.com/gkalpak",
|
||||
"bio": "George is a Software Engineer with a passion for chess, robotics and automating stuff. He has a strong need to know how things work (so if you already know, he'd love to have a talk with you). He has been a member of the AngularJS team since 2014. When not doing geeky stuff, he is probably trying to convince his wife and kids to apply programming principles in real life. (Or is it the other way around?)",
|
||||
"type": "Community"
|
||||
},
|
||||
"kapunahelewong": {
|
||||
"name": "Kapunahele Wong",
|
||||
"picture": "/resources/images/bios/kapunahelewong.jpg",
|
||||
"website": " https://github.com/kapunahelewong",
|
||||
"bio": "Kapunahele is a front-end developer at Capital One. She loves just about anything to do with JavaScript, Angular and electronics. She enjoys mapping Hawaiian star names and constellations to Western ones and loves dancing native Hawaiian hula.",
|
||||
"type": "Community"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
package.json
11
package.json
|
@ -2,7 +2,7 @@
|
|||
"name": "angular.io",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Angular 2 documentation",
|
||||
"description": "Angular documentation",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
|
@ -36,20 +36,19 @@
|
|||
"cross-spawn": "^4.0.0",
|
||||
"del": "^2.2.0",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.13.1",
|
||||
"dgeni-packages": "^0.16.0",
|
||||
"diff": "^2.1.3",
|
||||
"fs-extra": "^0.30.0",
|
||||
"globby": "^4.0.0",
|
||||
"grunt-sass": "^1.2.0",
|
||||
"gulp": "^3.5.6",
|
||||
"gulp-env": "0.4.0",
|
||||
"gulp-sass": "^2.3.2",
|
||||
"gulp-less": "^3.1.0",
|
||||
"gulp-sass": "^2.3.2",
|
||||
"gulp-task-listing": "^1.0.1",
|
||||
"gulp-tslint": "^5.0.0",
|
||||
"gulp-util": "^3.0.6",
|
||||
"gulp-watch": "^4.3.4",
|
||||
"harp": "^0.20.3",
|
||||
"harp": "0.21.0-pre.1",
|
||||
"html2jade": "^0.8.4",
|
||||
"indent-string": "^2.1.0",
|
||||
"jasmine-core": "^2.3.4",
|
||||
|
@ -71,7 +70,7 @@
|
|||
"protractor": "^3.0.0",
|
||||
"q": "^1.4.1",
|
||||
"tree-kill": "^1.0.0",
|
||||
"tslint": "^3.2.2",
|
||||
"tslint": "^3.15.1",
|
||||
"yargs": "^4.7.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"index": {
|
||||
"hero": "home",
|
||||
"title": "One framework.",
|
||||
"subtitle": "Mobile and desktop."
|
||||
"subtitle": "Mobile & desktop."
|
||||
},
|
||||
|
||||
"features": {
|
||||
|
|
|
@ -8,7 +8,7 @@ else
|
|||
- var styleguide = "/docs/ts/latest/styleguide.html"
|
||||
|
||||
.main-footer
|
||||
nav.background-blue-grey-900.grid-fluid
|
||||
nav.background-midnight.grid-fluid
|
||||
|
||||
.c3.main-footer-branding
|
||||
.logo-inverse-large
|
||||
|
@ -17,7 +17,7 @@ else
|
|||
h3.text-headline RESOURCES
|
||||
|
||||
ul.text-body
|
||||
// TODO: (ericjim) make a libraries page to showcase all angular 2 libraries
|
||||
// TODO: (ericjim) make a libraries page to showcase all angular libraries
|
||||
//li <a href="/libraries.html">Libraries</a>
|
||||
li <a href="/about/">About</a>
|
||||
li <a href="/resources/">Books & Training</a>
|
||||
|
@ -51,7 +51,7 @@ else
|
|||
ul.text-body
|
||||
li <a href="https://angular.cn/">中文版</a>
|
||||
|
||||
footer(class="background-steel")
|
||||
small.text-caption Powered by Google ©2010-2016. Code licensed under an <a href="/license" class="text-snow">MIT-style License</a>. Documentation licensed under <a class="text-snow" href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||||
a(aria-label="View Style Guide" href=styleguide title="Style Guide" class="styleguide-trigger text-snow")
|
||||
footer(class="background-midnight")
|
||||
small.text-caption Powered by Google ©2010-2016. Code licensed under an <a href="/license">MIT-style License</a>. Documentation licensed under <a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||||
a(aria-label="View Style Guide" href=styleguide title="Style Guide" class="styleguide-trigger")
|
||||
span.icon-favorite
|
||||
|
|
|
@ -12,7 +12,7 @@ if title == "Angular"
|
|||
else if language
|
||||
title #{title} - #{language}
|
||||
else
|
||||
title #{title} - Angular 2
|
||||
title #{title} - Angular
|
||||
|
||||
meta(charset="utf-8")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
|
@ -22,14 +22,14 @@ meta(name="robots" content="all")
|
|||
meta(name="referrer" content="origin")
|
||||
meta(name="viewport" id="viewport" content="width=device-width, initial-scale=1")
|
||||
|
||||
meta(property="og:title" content="Angular 2")
|
||||
meta(property="og:title" content="Angular")
|
||||
meta(property="og:image" content="/resources/images/logos/standard/shield-large.png")
|
||||
meta(property="og:image:type" content="image/png")
|
||||
meta(property="og:image:width" content="184")
|
||||
meta(property="og:image:height" content="200")
|
||||
meta(property="og:description" content="#{description}")
|
||||
|
||||
meta(itemprop="name" content="Angular 2")
|
||||
meta(itemprop="name" content="Angular")
|
||||
meta(itemprop="description" content="#{description}")
|
||||
meta(itemprop="image" content="https://angular.io/resources/images/logos/standard/shield-large.png")
|
||||
|
||||
|
@ -57,4 +57,4 @@ link(rel="stylesheet" href="/resources/css/main.css")
|
|||
<link rel="icon" type="image/png" href="/resources/images/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="/resources/images/favicons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="/resources/images/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/resources/images/favicons/manifest.json">
|
||||
<link rel="manifest" href="/resources/images/favicons/manifest.json">
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
header(class="background-sky")
|
||||
.hero.background-superhero-paper.is-large
|
||||
h1.text-headline.hero-logo #{title}<br>#{subtitle}
|
||||
header(class="background-sky l-relative")
|
||||
|
||||
.hero-cta
|
||||
a(href="/docs/ts/latest/quickstart.html" class="md-raised button button-large button-plain" md-button) Get Started
|
||||
.hero.background-superhero-paper.is-large
|
||||
img(class="hero-logo" src='/resources/images/logos/angular2/angular.svg')
|
||||
h1.text-headline #{title}<br>#{subtitle}
|
||||
a(href="/docs/ts/latest/quickstart.html" class="hero-cta md-raised button button-large button-plain" md-button) Get Started
|
||||
|
||||
.banner.banner-floaty
|
||||
.banner-ng-annoucement
|
||||
div(class="banner-text" align="center")
|
||||
p Join us for AngularConnect in London, UK this September!
|
||||
div(class="banner-button")
|
||||
a(href="http://angularconnect.com/?utm_source=angular&utm_medium=banner&utm_campaign=angular-banner" target="_blank" class="button md-button") Register now
|
||||
|
||||
announcement-bar
|
||||
.announcement-bar-slide.clearfix
|
||||
img(src="/resources/images/logos/angular2/angular-logo-banner.png")
|
||||
p Angular 2.0 Final Release Now Live!
|
||||
a(href="http://angularjs.blogspot.com/2016/09/angular2-final.html" target="_blank" class="button " + "md-button") Learn More
|
||||
|
||||
.announcement-bar-slide.clearfix
|
||||
img(src="/resources/images/logos/ng-europe/ng-europe-logo.png")
|
||||
p Join us for <strong>ng-europe in Paris</strong>, France this October!
|
||||
a(href="https://ngeurope.org/?utm_source=angular&utm_medium=banner&utm_campaign=angular-banner" target="_blank" class="button md-button") Register now
|
|
@ -13,27 +13,27 @@
|
|||
if current.path[4] && current.path[3] == 'api'
|
||||
- var textFormat = 'is-standard-case'
|
||||
|
||||
header(class="hero background-sky", style=fixHeroCss ? "height:auto" : "")
|
||||
div(class="inner-header")
|
||||
h1(class="hero-title text-display-1 #{textFormat}") #{headerTitle}
|
||||
if useBadges
|
||||
span(class="badges")
|
||||
if docType
|
||||
span(class="status-badge").
|
||||
#{renamer(capitalize(docType))}
|
||||
if stability
|
||||
span(layout="row" class="status-badge")
|
||||
// badge circle is filled based on stability by matching a css selector in _hero.scss
|
||||
span(class="status-circle status-#{stability}")
|
||||
span Stability: #{capitalize(stability)}
|
||||
if security
|
||||
span(class="status-badge security-risk-badge").
|
||||
Security Risk
|
||||
header.hero.background-sky
|
||||
h1(class="hero-title #{textFormat}") #{headerTitle}
|
||||
|
||||
if useBadges
|
||||
if stability
|
||||
span(class="badge is-#{stability}").
|
||||
#{capitalize(stability)}
|
||||
if security
|
||||
span(class="badge is-deprecated").
|
||||
Security Risk
|
||||
|
||||
//CLEAR FLOAT ELEMENTS
|
||||
.clear
|
||||
|
||||
if subtitle
|
||||
h2.hero-subtitle.text-subhead #{subtitle}
|
||||
h2.hero-subtitle #{subtitle}
|
||||
|
||||
else if docType
|
||||
h2.hero-subtitle #{renamer(capitalize(docType))}
|
||||
|
||||
|
||||
if current.path[3] == 'api' && current.path[1] == 'dart'
|
||||
block breadcrumbs
|
||||
|
||||
else if current.path[3] == 'api' && current.path[1] == 'dart'
|
||||
block breadcrumbs
|
||||
else if current.path[0] == "docs"
|
||||
!= partial("_version-dropdown")
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
- var language = current.path[1] || 'ts'
|
||||
- if (language !== 'ts' || language !== 'js' || language !== 'dart') { language = 'ts'; }
|
||||
|
||||
md-toolbar(class="main-nav background-regal l-pinned-top l-layer-5",scroll-y-offset-element)
|
||||
nav
|
||||
h1 <a href="/" md-button>Angular <sup>by Google</sup></a>
|
||||
nav(class="main-nav l-pinned-top l-layer-5", scroll-y-offset-element)
|
||||
h1 <a href="/" md-button>Angular <sup>by Google</sup></a>
|
||||
|
||||
button(class="main-nav-button main-nav-mobile-trigger l-right" aria-label="View Menu" ng-click="appCtrl.toggleMainMenu($event)" md-button) Site Menu <span class="icon icon-arrow-drop-down"></span>
|
||||
button(class="main-nav-button main-nav-mobile-trigger l-right" aria-label="View Menu" ng-click="appCtrl.toggleMainMenu($event)" md-button) Site Menu <span class="icon icon-arrow-drop-down"></span>
|
||||
|
||||
ul(ng-class="appCtrl.showMainNav ? 'is-visible' : ''")
|
||||
li.l-left <a class="main-nav-button" href="/features.html" md-button>Features</a>
|
||||
li.l-left <a class="main-nav-button" href="/docs/#{language}/latest/" md-button>Docs</a>
|
||||
li.l-left <a class="main-nav-button" href="/events.html" md-button>Events</a>
|
||||
li.l-left <a class="main-nav-button" href="/news.html" md-button>News</a>
|
||||
li.l-right <a class="main-nav-button" href="/docs/ts/latest/quickstart.html" md-button>Get Started</a>
|
||||
ul(ng-class="appCtrl.showMainNav ? 'is-visible' : ''")
|
||||
li.l-left <a class="main-nav-button" href="/features.html" md-button>Features</a>
|
||||
li.l-left <a class="main-nav-button" href="/docs/#{language}/latest/" md-button>Docs</a>
|
||||
li.l-left <a class="main-nav-button" href="/events.html" md-button>Events</a>
|
||||
li.l-left <a class="main-nav-button" href="/news.html" md-button>News</a>
|
||||
li.l-right <a class="main-nav-button" href="/docs/ts/latest/quickstart.html" md-button>Get Started</a>
|
||||
|
|
|
@ -28,6 +28,7 @@ script(src="/resources/js/directives/cheatsheet.js")
|
|||
script(src="/resources/js/directives/api-list.js")
|
||||
script(src="/resources/js/directives/bio.js")
|
||||
script(src="/resources/js/directives/bold.js")
|
||||
script(src="/resources/js/directives/announcement-bar.js")
|
||||
script(src="/resources/js/directives/code.js")
|
||||
script(src="/resources/js/directives/copy.js")
|
||||
script(src="/resources/js/directives/code-tabs.js")
|
||||
|
@ -64,4 +65,4 @@ script(src="//www.gstatic.com/feedback/api.js" type="text/javascript")
|
|||
|
||||
<!-- Twitter Widget -->
|
||||
script.
|
||||
(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}})(document,"script","twitter-wjs");
|
||||
(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}})(document,"script","twitter-wjs");
|
||||
|
|
|
@ -38,6 +38,15 @@
|
|||
//- Location of sample code
|
||||
- var _liveLink = 'live link';
|
||||
|
||||
//- NgModule related
|
||||
- var _AppModuleVsAppComp = 'AppModule'
|
||||
- var _appModuleTsVsAppCompTs = 'app/app.module.ts'
|
||||
- var _appModuleTsVsMainTs = 'app/app.module.ts'
|
||||
- var _bootstrapModule = 'bootstrapModule'
|
||||
- var _moduleVsComp = 'module'
|
||||
- var _moduleVsRootComp = 'module'
|
||||
- var _platformBrowserDynamicVsBootStrap = 'platformBrowserDynamic'
|
||||
|
||||
//- Other
|
||||
- var _truthy = 'truthy';
|
||||
- var _falsey = 'falsey';
|
||||
|
@ -74,14 +83,14 @@ mixin makeExample(_filePath, region, _title, stylePatterns)
|
|||
- var format = attributes.format || defaultFormat;
|
||||
- if (attributes.format === '.') format = '';
|
||||
- var avoid = !!attributes.avoid;
|
||||
- var avoidClass = avoid ? 'is-anti-pattern' : '';
|
||||
|
||||
if (title)
|
||||
if (avoid)
|
||||
.example-title.avoid AVOID: #{title}
|
||||
else
|
||||
.example-title #{title}
|
||||
code-example(language="#{language}" format="#{format}")
|
||||
!= styleString(frag, stylePatterns)
|
||||
div(class="code-example #{avoidClass}")
|
||||
if (title)
|
||||
header
|
||||
h4 #{title}
|
||||
code-example(language="#{language}" format="#{format}")
|
||||
!= styleString(frag, stylePatterns)
|
||||
|
||||
//- Like makeExample, but: (1) doesn't show line numbers. (2) If region
|
||||
//- is omitted and title is 'foo (r)' then region is taken as 'r'.
|
||||
|
@ -89,14 +98,14 @@ mixin makeExample(_filePath, region, _title, stylePatterns)
|
|||
//- ending is given or is just (), then the title will be suffixed with
|
||||
//- either "(excerpt)", or "(#{_region})" when _region is defined.
|
||||
mixin makeExcerpt(_filePath, _region, _title, stylePatterns)
|
||||
- var matches = _filePath.match(/(.*)\s+\(([\w ]*)\)$/);
|
||||
- var matches = _filePath.match(/(.*)\s+\(([^\)]*)\)$/);
|
||||
- var parenText;
|
||||
- if (matches) { _filePath = matches[1]; parenText = matches[2]; }
|
||||
- var adjustments = adjustExamplePathAndTitle({filePath:_filePath, title:_title});
|
||||
- var filePath = adjustments.filePath;
|
||||
- var title = adjustments.title;
|
||||
- var region = _region || parenText;
|
||||
- var excerpt = !region || parenText === '' ? 'excerpt' : parenText || region;
|
||||
- var region = _region || (_region === '' ? '' : parenText);
|
||||
- var excerpt = parenText || region || 'excerpt';
|
||||
- if (title) title = title + ' (' + excerpt + ')';
|
||||
+makeExample(filePath, region, title, stylePatterns)(format='.')
|
||||
|
||||
|
@ -133,76 +142,78 @@ mixin makeJson( filePath, jsonConfig, title, stylePatterns)
|
|||
- var json = unescapeHtml(frag);
|
||||
- var jsonExtract = extractJson(json, jsonConfig);
|
||||
- var avoid = !!attributes.avoid;
|
||||
- var avoidClass = avoid ? 'is-anti-pattern' : '';
|
||||
|
||||
if (title)
|
||||
if (avoid)
|
||||
.example-title.avoid #{title}
|
||||
else
|
||||
.example-title #{title}
|
||||
code-example(language="#{language}" format="#{format}")
|
||||
if (jsonExtract == 'ERROR')
|
||||
err ERROR: Unable to extract json using config: "#{jsonConfig.toString()}"
|
||||
else
|
||||
!= styleString(jsonExtract, stylePatterns)
|
||||
div(class="code-example #{avoidClass}")
|
||||
if (title)
|
||||
header
|
||||
h4 #{title}
|
||||
code-example(language="#{language}" format="#{format}")
|
||||
if (jsonExtract == 'ERROR')
|
||||
err ERROR: Unable to extract json using config: "#{jsonConfig.toString()}"
|
||||
else
|
||||
!= styleString(jsonExtract, stylePatterns)
|
||||
|
||||
- // Open (and close) an explanation <div>. See QuickStart
|
||||
script.
|
||||
function why(id, backTo) {
|
||||
var id = "#"+id;
|
||||
var el = document.querySelector(id);
|
||||
el.hidden=el.hidden=!el.hidden;
|
||||
if !jade2ng
|
||||
//- Open (and close) an explanation <div>. See QuickStart
|
||||
script.
|
||||
function why(id, backTo) {
|
||||
var id = "#"+id;
|
||||
var el = document.querySelector(id);
|
||||
el.hidden=el.hidden=!el.hidden;
|
||||
|
||||
if (el.hidden && backTo){
|
||||
// the next line is required to work around a bug in WebKit (Chrome / Safari)
|
||||
location.href = "#";
|
||||
location.href = "#" + backTo;
|
||||
if (el.hidden && backTo){
|
||||
// the next line is required to work around a bug in WebKit (Chrome / Safari)
|
||||
location.href = "#";
|
||||
location.href = "#" + backTo;
|
||||
}
|
||||
}
|
||||
}
|
||||
script.
|
||||
function verbose(isVerbose) {
|
||||
isVerbose = !! isVerbose;
|
||||
var el = document.querySelector('button.verbose.off');
|
||||
el.style.display = isVerbose ? 'block' : 'none';
|
||||
var el = document.querySelector('button.verbose.on');
|
||||
el.style.display = isVerbose ? 'none' : 'block';
|
||||
script.
|
||||
function verbose(isVerbose) {
|
||||
isVerbose = !! isVerbose;
|
||||
var el = document.querySelector('button.verbose.off');
|
||||
el.style.display = isVerbose ? 'block' : 'none';
|
||||
var el = document.querySelector('button.verbose.on');
|
||||
el.style.display = isVerbose ? 'none' : 'block';
|
||||
|
||||
CCSStylesheetRuleStyle('main','.l-verbose-section', 'display',
|
||||
isVerbose ? 'block' : 'none');
|
||||
}
|
||||
CCSStylesheetRuleStyle('main','.l-verbose-section', 'display',
|
||||
isVerbose ? 'block' : 'none');
|
||||
}
|
||||
|
||||
script.
|
||||
function CCSStylesheetRuleStyle(stylesheet, selectorText, style, value){
|
||||
/* returns the value of the element style of the rule in the stylesheet
|
||||
* If no value is given, reads the value
|
||||
* If value is given, the value is changed and returned
|
||||
* If '' (empty string) is given, erases the value.
|
||||
* The browser will apply the default one
|
||||
*
|
||||
* string stylesheet: part of the .css name to be recognized, e.g. 'default'
|
||||
* string selectorText: css selector, e.g. '#myId', '.myClass', 'thead td'
|
||||
* string style: camelCase element style, e.g. 'fontSize'
|
||||
* string value optional : the new value
|
||||
*/
|
||||
var CCSstyle = undefined, rules, sheet;
|
||||
for(var m in document.styleSheets){
|
||||
sheet = document.styleSheets[m];
|
||||
if(sheet.href && sheet.href.indexOf(stylesheet) != -1){
|
||||
rules = sheet[document.all ? 'rules' : 'cssRules'];
|
||||
for(var n in rules){
|
||||
console.log(rules[n].selectorText);
|
||||
if(rules[n].selectorText == selectorText){
|
||||
CCSstyle = rules[n].style;
|
||||
break;
|
||||
script.
|
||||
function CCSStylesheetRuleStyle(stylesheet, selectorText, style, value){
|
||||
/* returns the value of the element style of the rule in the stylesheet
|
||||
* If no value is given, reads the value
|
||||
* If value is given, the value is changed and returned
|
||||
* If '' (empty string) is given, erases the value.
|
||||
* The browser will apply the default one
|
||||
*
|
||||
* string stylesheet: part of the .css name to be recognized, e.g. 'default'
|
||||
* string selectorText: css selector, e.g. '#myId', '.myClass', 'thead td'
|
||||
* string style: camelCase element style, e.g. 'fontSize'
|
||||
* string value optional : the new value
|
||||
*/
|
||||
var CCSstyle = undefined, rules, sheet;
|
||||
for(var m in document.styleSheets){
|
||||
sheet = document.styleSheets[m];
|
||||
if(sheet.href && sheet.href.indexOf(stylesheet) != -1){
|
||||
rules = sheet[document.all ? 'rules' : 'cssRules'];
|
||||
for(var n in rules){
|
||||
console.log(rules[n].selectorText);
|
||||
if(rules[n].selectorText == selectorText){
|
||||
CCSstyle = rules[n].style;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(value == undefined)
|
||||
return CCSstyle[style]
|
||||
else
|
||||
return CCSstyle[style] = value
|
||||
}
|
||||
if(value == undefined)
|
||||
return CCSstyle[style]
|
||||
else
|
||||
return CCSstyle[style] = value
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
//- Converts the given project-relative path (like 'app/main.ts')
|
||||
//- to a doc folder relative path (like 'quickstart/ts/app/main.ts')
|
||||
|
@ -213,8 +224,8 @@ script.
|
|||
- // E.g. of a project relative path is 'app/main.ts'
|
||||
- if (ex.title === null || ex.title === undefined) {
|
||||
- // Title is not given so take it to be ex.filePath.
|
||||
- // Is title like styles.1.css? Then drop the '.1' qualifier:
|
||||
- var matches = ex.filePath.match(/^(.*)\.\d(\.\w+)$/);
|
||||
- // Title like styles.1.css or foo_1.dart? Then drop the '.1' or '_1' qualifier:
|
||||
- var matches = ex.filePath.match(/^(.*)[\._]\d(\.\w+)$/);
|
||||
- ex.title = matches ? matches[1] + matches[2] : ex.filePath;
|
||||
- }
|
||||
- ex.filePath = getExampleName() + '/' + _docsFor + '/' + ex.filePath;
|
||||
|
@ -289,7 +300,7 @@ script.
|
|||
- } else {
|
||||
- // ``` gets translated to <pre><code>.....</code></pre> and we need
|
||||
- // to remove this from the fragment prefix is 11 long and suffix is 13 long
|
||||
- frag = frag.substring(11, frag.length-13);
|
||||
- frag = jade2ng ? frag : frag.substring(11, frag.length-13);
|
||||
- // Uncomment next line for debugging.
|
||||
- // frag = "FileName: " + fullFileName + " Current path: " + current.path + " PathToDocs: " + getPathToDocs() + "\n" + frag;
|
||||
- return frag;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
- var version = ''
|
||||
- var page = ''
|
||||
|
||||
<!-- Replace _ underscores with . dots -->
|
||||
//- Replace _ underscores with . dots
|
||||
if current.path[2]
|
||||
- var version = current.path[2].replace(/\_+/gm, ".")
|
||||
|
||||
|
@ -33,32 +33,31 @@ else if current.path[3]
|
|||
else
|
||||
- var page = current.path[3] + '.html'
|
||||
|
||||
<!-- VERSION TREE CREATOR MIXIN -->
|
||||
//- VERSION TREE CREATOR MIXIN
|
||||
mixin tree(directory, urlPrefix, name, latest)
|
||||
ul
|
||||
for val, semvar in directory
|
||||
if semvar !== '.git' && semvar !== '_data'
|
||||
- var libVersion = (semvar == "latest") ? latest : semvar.replace(/\_+/gm, ".")
|
||||
li <a href="#{urlPrefix}/#{semvar}/#{page}" md-button>#{name} #{libVersion}</a>
|
||||
for val, semvar in directory
|
||||
if semvar !== '.git' && semvar !== '_data'
|
||||
- var libVersion = (semvar == "latest") ? latest : semvar.replace(/\_+/gm, ".")
|
||||
li <a href="#{urlPrefix}/#{semvar}/#{page}" md-button>#{name} #{libVersion}</a>
|
||||
|
||||
|
||||
<!-- BUTTON TITLE GENERATION -->
|
||||
//- BUTTON TITLE GENERATION
|
||||
if language == 'ts'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for TypeScript'
|
||||
- var title = 'Angular for TypeScript'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for TypeScript'
|
||||
|
||||
if language == 'js'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for JavaScript'
|
||||
- var title = 'Angular for JavaScript'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for JavaScript'
|
||||
|
||||
|
||||
if language == 'dart'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for Dart'
|
||||
- var title = 'Angular for Dart'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for Dart'
|
||||
|
||||
|
@ -69,7 +68,9 @@ nav.dropdown
|
|||
div(class="overlay ng-hide" ng-click="appCtrl.toggleVersionMenu($event)" ng-show="appCtrl.showMenu")
|
||||
|
||||
<!-- DROPDOWN MENU -->
|
||||
div(class="dropdown-menu" ng-class="appCtrl.showMenu ? 'is-visible' : ''")
|
||||
mixin tree(public.docs.ts, "/docs/ts", "Angular 2 for TypeScript")
|
||||
mixin tree(public.docs.js, "/docs/js", "Angular 2 for JavaScript")
|
||||
mixin tree(public.docs.dart, "/docs/dart", "Angular 2 for Dart")
|
||||
ul(class="dropdown-menu" ng-class="appCtrl.showMenu ? 'is-visible' : ''")
|
||||
mixin tree(public.docs.ts, "/docs/ts", "Angular for TypeScript")
|
||||
mixin tree(public.docs.js, "/docs/js", "Angular for JavaScript")
|
||||
//- Disable cross-language link for API entry pages (but keep for top API search page):
|
||||
if ! (current.path[3] === 'api' && public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]])
|
||||
mixin tree(public.docs.dart, "/docs/dart", "Angular for Dart")
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
p We'd love for you to contribute to our source code and to make Angular projects even better.
|
||||
|
||||
.l-sub-section
|
||||
h3 Angular 2
|
||||
h3 Angular
|
||||
|
||||
p Angular 2 is a next generation mobile and desktop application development platform.
|
||||
p Angular is a next generation mobile and desktop application development platform.
|
||||
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) Contribute to Angular 2
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) Contribute to Angular
|
||||
|
||||
.l-sub-section
|
||||
h3 Angular for JavaScript or Dart
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"protractor": "^3.3.0",
|
||||
"typings": "^1.0.4"
|
||||
"typings": "^1.0.4",
|
||||
"ts-node": "^1.3.0",
|
||||
"typescript": "^2.0.2"
|
||||
},
|
||||
"repository": {}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ exports.config = {
|
|||
// Framework to use. Jasmine is recommended.
|
||||
framework: 'jasmine',
|
||||
|
||||
// For angular2 tests
|
||||
// For angular tests
|
||||
useAllAngular2AppRoots: true,
|
||||
|
||||
// Base URL for application server
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* The tests here basically just checking that the end styles
|
||||
* of each animation are in effect.
|
||||
*
|
||||
* Relies on the Angular 2 testability only becoming stable once
|
||||
* Relies on the Angular testability only becoming stable once
|
||||
* animation(s) have finished.
|
||||
*
|
||||
* Ideally we'd use https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations
|
||||
|
@ -289,6 +289,19 @@ describe('Animation Tests', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('callbacks', () => {
|
||||
it('fires a callback on start and done', () => {
|
||||
addActiveHero();
|
||||
browser.manage().logs().get('browser').then((logs) => {
|
||||
const animationMessages = logs.filter((log) => {
|
||||
return log.message.indexOf('Animation') !== -1 ? true : false;
|
||||
});
|
||||
|
||||
expect(animationMessages.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function addActiveHero(sleep?: number) {
|
||||
sleep = sleep || 500;
|
||||
element(by.buttonText('Add active hero')).click();
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@shrinkOut="'in'">
|
||||
[@shrinkOut]="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -27,7 +27,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
[@heroState]="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
|
|
|
@ -20,7 +20,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
[@heroState]="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Heroes } from './hero.service';
|
|||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
(click)="hero.toggleState()"
|
||||
@heroState="hero.state">
|
||||
[@heroState]="hero.state">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
[@flyInOut]="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
[@flyInOut]="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -19,7 +19,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
[@heroState]="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
style,
|
||||
animate,
|
||||
transition,
|
||||
keyframes
|
||||
keyframes,
|
||||
AnimationTransitionEvent
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
@ -14,14 +15,18 @@ import { Heroes } from './hero.service';
|
|||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-multistep',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
(@flyInOut.start)="animationStarted($event)"
|
||||
(@flyInOut.done)="animationDone($event)"
|
||||
[@flyInOut]="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/* The element here always has the state "in" when it
|
||||
* is present. We animate two transitions: From void
|
||||
|
@ -54,4 +59,12 @@ import { Heroes } from './hero.service';
|
|||
})
|
||||
export class HeroListMultistepComponent {
|
||||
@Input() heroes: Heroes;
|
||||
|
||||
animationStarted(event: AnimationTransitionEvent) {
|
||||
console.warn('Animation started: ', event);
|
||||
}
|
||||
|
||||
animationDone(event: AnimationTransitionEvent) {
|
||||
console.warn('Animation done: ', event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'"
|
||||
[@flyInOut]="'in'"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
|
|
|
@ -20,7 +20,7 @@ import { Heroes } from './hero.service';
|
|||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
[@heroState]="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<!-- Polyfill for Web Animations -->
|
||||
<script src="https://npmcdn.com/web-animations-js@2.2.1"></script>
|
||||
<script src="https://unpkg.com/web-animations-js@2.2.1"></script>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Angular 2 Animations",
|
||||
"description": "Angular Animations",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
|
|
|
@ -5,7 +5,7 @@ version: 0.0.1
|
|||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
dependencies:
|
||||
angular2: 2.0.0-beta.18
|
||||
angular2: 2.0.0-beta.21
|
||||
browser: ^0.10.0
|
||||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -10,7 +10,7 @@ class Hero {
|
|||
|
||||
describe('Architecture', () => {
|
||||
|
||||
const expectedTitle = 'Architecture of Angular 2';
|
||||
const expectedTitle = 'Architecture of Angular';
|
||||
const expectedH2 = ['Hero List', 'Sales Tax Calculator'];
|
||||
|
||||
beforeAll(() => browser.get(''));
|
||||
|
|
|
@ -13,7 +13,7 @@ const HEROES = [
|
|||
export class BackendService {
|
||||
constructor(private logger: Logger) {}
|
||||
|
||||
getAll(type: Type): PromiseLike<any[]> {
|
||||
getAll(type: Type<any>): PromiseLike<any[]> {
|
||||
if (type === Hero) {
|
||||
// TODO get from the database
|
||||
return Promise.resolve<Hero[]>(HEROES);
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Component } from '@angular/core';
|
|||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: 'Welcome to Angular 2'
|
||||
template: 'Welcome to Angular'
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(logger: Logger) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Intro to Angular2",
|
||||
"description": "Intro to Angular",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
|
|
|
@ -5,7 +5,7 @@ version: 0.0.1
|
|||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
dependencies:
|
||||
angular2: 2.0.0-beta.18
|
||||
angular2: 2.0.0-beta.21
|
||||
browser: ^0.10.0
|
||||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
// #docregion
|
||||
import { Directive, ElementRef, Input } from '@angular/core';
|
||||
import { Directive, ElementRef, Input, Renderer } from '@angular/core';
|
||||
|
||||
@Directive({ selector: '[myHighlight]' })
|
||||
export class HighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'yellow';
|
||||
constructor(el: ElementRef, renderer: Renderer) {
|
||||
renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'yellow');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
// #docregion
|
||||
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener, Input, Renderer } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[myHighlight]'
|
||||
|
@ -8,9 +8,7 @@ import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
|||
|
||||
export class HighlightDirective {
|
||||
// #docregion ctor
|
||||
private el: HTMLElement;
|
||||
|
||||
constructor(el: ElementRef) { this.el = el.nativeElement; }
|
||||
constructor(private el: ElementRef, private renderer: Renderer) { }
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion mouse-methods, host
|
||||
|
@ -28,7 +26,7 @@ export class HighlightDirective {
|
|||
// #enddocregion host
|
||||
|
||||
private highlight(color: string) {
|
||||
this.el.style.backgroundColor = color;
|
||||
this.renderer.setElementStyle(this.el.nativeElement, 'backgroundColor', color);
|
||||
}
|
||||
// #enddocregion mouse-methods
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// #docplaster
|
||||
// #docregion full
|
||||
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener, Input, Renderer } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[myHighlight]'
|
||||
|
@ -8,9 +8,8 @@ import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
|||
// #docregion class
|
||||
export class HighlightDirective {
|
||||
private _defaultColor = 'red';
|
||||
private el: HTMLElement;
|
||||
|
||||
constructor(el: ElementRef) { this.el = el.nativeElement; }
|
||||
constructor(private el: ElementRef, private renderer: Renderer) { }
|
||||
// #enddocregion class
|
||||
|
||||
// #docregion defaultColor
|
||||
|
@ -34,7 +33,7 @@ export class HighlightDirective {
|
|||
}
|
||||
|
||||
private highlight(color: string) {
|
||||
this.el.style.backgroundColor = color;
|
||||
this.renderer.setElementStyle(this.el.nativeElement, 'backgroundColor', color);
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
|
|
|
@ -2,17 +2,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { MovieListComponent } from './movie-list.component';
|
||||
import { routes } from './app.routes';
|
||||
import { routing } from './app.routing';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
RouterModule.forRoot(routes, {})
|
||||
routing
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// #docregion
|
||||
import { RouterConfig } from '@angular/router';
|
||||
|
||||
import { MovieListComponent } from './movie-list.component';
|
||||
|
||||
export const routes: RouterConfig = [
|
||||
{ path: '', redirectTo: '/movies', pathMatch: 'full' },
|
||||
{ path: 'movies', component: MovieListComponent }
|
||||
];
|
|
@ -0,0 +1,12 @@
|
|||
// #docregion
|
||||
import { ModuleWithProviders } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { MovieListComponent } from './movie-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/movies', pathMatch: 'full' },
|
||||
{ path: 'movies', component: MovieListComponent }
|
||||
];
|
||||
|
||||
export const routing: ModuleWithProviders = RouterModule.forRoot(routes);
|
Binary file not shown.
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,25 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
'use strict';
|
||||
/* tslint:disable:quotemark */
|
||||
describe('AOT Compilation', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should load page and click button', function (done) {
|
||||
let headingSelector = element.all(by.css('h1')).get(0);
|
||||
expect(headingSelector.getText()).toEqual('My First Angular App');
|
||||
|
||||
expect(element.all(by.xpath('//div[text()="Magneta"]')).get(0).isPresent()).toBe(true);
|
||||
expect(element.all(by.xpath('//div[text()="Bombasto"]')).get(0).isPresent()).toBe(true);
|
||||
expect(element.all(by.xpath('//div[text()="Magma"]')).get(0).isPresent()).toBe(true);
|
||||
expect(element.all(by.xpath('//div[text()="Tornado"]')).get(0).isPresent()).toBe(true);
|
||||
|
||||
let toggleButton = element.all(by.css('button')).get(0);
|
||||
toggleButton.click().then(function() {
|
||||
expect(headingSelector.isPresent()).toBe(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
**/*.ngfactory.ts
|
||||
**/*.metadata.json
|
||||
dist
|
||||
!app/tsconfig.json
|
||||
!rollup.js
|
|
@ -0,0 +1,7 @@
|
|||
<!-- #docregion -->
|
||||
<button (click)="toggleHeading()">Toggle Heading</button>
|
||||
<h1 *ngIf="showHeading">My First Angular App</h1>
|
||||
|
||||
<h3>List of Heroes</h3>
|
||||
<div *ngFor="let hero of heroes">{{hero}}</div>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// #docregion
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: 'app.component.html'
|
||||
})
|
||||
export class AppComponent {
|
||||
showHeading = true;
|
||||
heroes = ['Magneta', 'Bombasto', 'Magma', 'Tornado'];
|
||||
|
||||
toggleHeading() {
|
||||
this.showHeading = !this.showHeading;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
|
@ -0,0 +1,6 @@
|
|||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -0,0 +1,6 @@
|
|||
// #docregion
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
|
||||
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"build": "build:aot",
|
||||
"run": "http-server:e2e"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<!-- #docregion -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Ahead of time compilation</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<!-- #docregion bundle -->
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
<script src="dist/build.js"></script>
|
||||
<!-- #enddocregion bundle -->
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
// #docregion
|
||||
import rollup from 'rollup'
|
||||
import nodeResolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import uglify from 'rollup-plugin-uglify'
|
||||
|
||||
// #docregion config
|
||||
export default {
|
||||
entry: 'app/main.js',
|
||||
dest: 'dist/build.js', // output a single application bundle
|
||||
sourceMap: false,
|
||||
format: 'iife',
|
||||
plugins: [
|
||||
nodeResolve({jsnext: true, module: true}),
|
||||
// #docregion commonjs
|
||||
commonjs({
|
||||
include: 'node_modules/rxjs/**',
|
||||
}),
|
||||
// #enddocregion commonjs
|
||||
// #docregion uglify
|
||||
uglify()
|
||||
// #enddocregion uglify
|
||||
]
|
||||
}
|
||||
// #enddocregion config
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "system",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
@ -9,5 +9,16 @@
|
|||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
},
|
||||
|
||||
"files": [
|
||||
"app/app.module.ts",
|
||||
"app/main.ts",
|
||||
"./typings/index.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "aot",
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
@ -30,12 +30,17 @@ let directives: any[] = [
|
|||
VoteTakerComponent
|
||||
];
|
||||
|
||||
let schemas: any[] = [];
|
||||
|
||||
// Include Countdown examples
|
||||
// unless in e2e tests which they break.
|
||||
if (!/e2e/.test(location.search)) {
|
||||
console.log('adding countdown timer examples');
|
||||
directives.push(CountdownLocalVarParentComponent);
|
||||
directives.push(CountdownViewChildParentComponent);
|
||||
} else {
|
||||
// In e2e test use CUSTOM_ELEMENTS_SCHEMA to supress unknown element errors
|
||||
schemas.push(CUSTOM_ELEMENTS_SCHEMA);
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
|
@ -43,6 +48,7 @@ if (!/e2e/.test(location.search)) {
|
|||
BrowserModule
|
||||
],
|
||||
declarations: directives,
|
||||
bootstrap: [ AppComponent ]
|
||||
bootstrap: [ AppComponent ],
|
||||
schemas: schemas
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { browserDynamicPlatform } from '@angular/platform-browser-dynamic';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
browserDynamicPlatform().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { SomeAbsoluteComponent, SomeRelativeComponent } from './some.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template:
|
||||
`<h1>Absolute & <i>Component-Relative</i> Paths</h1>
|
||||
<absolute-path></absolute-path>
|
||||
<relative-path></relative-path>
|
||||
`,
|
||||
directives: [SomeAbsoluteComponent, SomeRelativeComponent]
|
||||
`
|
||||
})
|
||||
export class AppComponent {}
|
||||
|
|
|
@ -2,13 +2,16 @@ import { NgModule } from '@angular/core';
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { SomeAbsoluteComponent, SomeRelativeComponent } from './some.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
SomeAbsoluteComponent,
|
||||
SomeRelativeComponent
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// #docregion
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { XHRBackend } from '@angular/http';
|
||||
// import { appRouterProviders } from './app.routes';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
/* import { routing } from './app.routing';*/
|
||||
import { LocationStrategy,
|
||||
HashLocationStrategy } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { HeroData } from './hero-data';
|
||||
import { InMemoryBackendService,
|
||||
SEED_DATA } from 'angular2-in-memory-web-api';
|
||||
import { InMemoryWebApiModule } from 'angular2-in-memory-web-api';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeroBioComponent } from './hero-bio.component';
|
||||
|
@ -31,44 +32,43 @@ import { ParentFinderComponent,
|
|||
BethComponent,
|
||||
BobComponent } from './parent-finder.component';
|
||||
|
||||
const DIRECTIVES = [
|
||||
const declarations = [
|
||||
AppComponent,
|
||||
HeroBiosComponent, HeroBiosAndContactsComponent, HeroBioComponent,
|
||||
HeroesBaseComponent, SortedHeroesComponent,
|
||||
HeroOfTheMonthComponent, HeroContactComponent,
|
||||
HighlightDirective,
|
||||
ParentFinderComponent,
|
||||
AppComponent
|
||||
];
|
||||
|
||||
const B_DIRECTIVES = [ BarryComponent, BethComponent, BobComponent ];
|
||||
const a_components = [AliceComponent, AlexComponent ];
|
||||
|
||||
// #docregion C_DIRECTIVES
|
||||
const C_DIRECTIVES = [
|
||||
const b_components = [ BarryComponent, BethComponent, BobComponent ];
|
||||
|
||||
const c_components = [
|
||||
CarolComponent, ChrisComponent, CraigComponent,
|
||||
CathyComponent
|
||||
];
|
||||
// #enddocregion C_DIRECTIVES
|
||||
|
||||
// #docregion bootstrap
|
||||
@NgModule({
|
||||
imports: [ BrowserModule, FormsModule ],
|
||||
declarations: [ ...DIRECTIVES,
|
||||
...B_DIRECTIVES,
|
||||
...C_DIRECTIVES,
|
||||
AliceComponent,
|
||||
AlexComponent ],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
InMemoryWebApiModule.forRoot(HeroData)
|
||||
// routing TODO: add routes
|
||||
],
|
||||
declarations: [
|
||||
declarations,
|
||||
a_components,
|
||||
b_components,
|
||||
c_components,
|
||||
],
|
||||
bootstrap: [ AppComponent ],
|
||||
// #docregion providers
|
||||
providers: [
|
||||
// appRouterProviders, TODO: add routes
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
|
||||
{ provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
|
||||
{ provide: SEED_DATA, useClass: HeroData } // in-mem server data
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy }
|
||||
]
|
||||
// #enddocregion providers
|
||||
})
|
||||
export class AppModule {
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
// #enddocregion bootstraps
|
||||
|
||||
export class AppModule { }
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { provideRouter, RouterConfig } from '@angular/router';
|
||||
|
||||
const routes: RouterConfig = [];
|
||||
|
||||
export const appRouterProviders = [
|
||||
provideRouter(routes)
|
||||
];
|
|
@ -0,0 +1,10 @@
|
|||
import { ModuleWithProviders } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
const routes: Routes = [];
|
||||
|
||||
export const routing: ModuleWithProviders = RouterModule.forRoot(routes);
|
||||
|
||||
export const appRoutingProviders: any[] = [
|
||||
|
||||
];
|
|
@ -2,7 +2,7 @@
|
|||
/* tslint:disable:*/
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, forwardRef, Optional, provide, SkipSelf } from '@angular/core';
|
||||
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
|
||||
|
||||
// A component base class (see AlexComponent)
|
||||
export abstract class Base { name = 'Count Basie'; }
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
'use strict'; // necessary for node!
|
||||
|
||||
// THESE TESTS ARE INCOMPLETE
|
||||
describeIf(browser.appIsTs || browser.appIsJs, 'Form Validation Tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
describe('Hero Form 1', () => {
|
||||
beforeAll(() => {
|
||||
getPage('hero-form-template1');
|
||||
});
|
||||
|
||||
tests();
|
||||
});
|
||||
|
||||
describe('Hero Form 2', () => {
|
||||
beforeAll(() => {
|
||||
getPage('hero-form-template2');
|
||||
});
|
||||
|
||||
tests();
|
||||
bobTests();
|
||||
});
|
||||
|
||||
describe('Hero Form 3 (Reactive)', () => {
|
||||
beforeAll(() => {
|
||||
getPage('hero-form-reactive3');
|
||||
makeNameTooLong();
|
||||
});
|
||||
|
||||
tests();
|
||||
bobTests();
|
||||
});
|
||||
});
|
||||
|
||||
//////////
|
||||
|
||||
const testName = 'Test Name';
|
||||
|
||||
let page: {
|
||||
section: protractor.ElementFinder,
|
||||
form: protractor.ElementFinder,
|
||||
title: protractor.ElementFinder,
|
||||
nameInput: protractor.ElementFinder,
|
||||
alterEgoInput: protractor.ElementFinder,
|
||||
powerSelect: protractor.ElementFinder,
|
||||
errorMessages: protractor.ElementArrayFinder,
|
||||
heroFormButtons: protractor.ElementArrayFinder,
|
||||
heroSubmitted: protractor.ElementFinder
|
||||
};
|
||||
|
||||
function getPage(sectionTag: string) {
|
||||
let section = element(by.css(sectionTag));
|
||||
let buttons = section.all(by.css('button'));
|
||||
|
||||
page = {
|
||||
section: section,
|
||||
form: section.element(by.css('form')),
|
||||
title: section.element(by.css('h1')),
|
||||
nameInput: section.element(by.css('#name')),
|
||||
alterEgoInput: section.element(by.css('#alterEgo')),
|
||||
powerSelect: section.element(by.css('#power')),
|
||||
errorMessages: section.all(by.css('div.alert')),
|
||||
heroFormButtons: buttons,
|
||||
heroSubmitted: section.element(by.css('hero-submitted > div'))
|
||||
};
|
||||
}
|
||||
|
||||
function tests() {
|
||||
it('should display correct title', function () {
|
||||
expect(page.title.getText()).toContain('Hero Form');
|
||||
});
|
||||
|
||||
it('should not display submitted message before submit', function () {
|
||||
expect(page.heroSubmitted.isElementPresent(by.css('h2'))).toBe(false);
|
||||
});
|
||||
|
||||
it('should have form buttons', function () {
|
||||
expect(page.heroFormButtons.count()).toEqual(2);
|
||||
});
|
||||
|
||||
it('should have error at start', function () {
|
||||
expectFormIsInvalid();
|
||||
});
|
||||
|
||||
// it('showForm', function () {
|
||||
// page.form.getInnerHtml().then(html => console.log(html));
|
||||
// });
|
||||
|
||||
it('should have disabled submit button', function () {
|
||||
expect(page.heroFormButtons.get(0).isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('resetting name to valid name should clear errors', function () {
|
||||
const ele = page.nameInput;
|
||||
expect(ele.isPresent()).toBe(true, 'nameInput should exist');
|
||||
ele.clear();
|
||||
ele.sendKeys(testName);
|
||||
expectFormIsValid();
|
||||
});
|
||||
|
||||
it('should produce "required" error after clearing name', function () {
|
||||
page.nameInput.clear();
|
||||
// page.alterEgoInput.click(); // to blur ... didn't work
|
||||
page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh!
|
||||
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
|
||||
expect(page.errorMessages.get(0).getText()).toContain('required');
|
||||
});
|
||||
|
||||
it('should produce "at least 4 characters" error when name="x"', function () {
|
||||
page.nameInput.clear();
|
||||
page.nameInput.sendKeys('x'); // too short
|
||||
expectFormIsInvalid();
|
||||
expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters');
|
||||
});
|
||||
|
||||
it('resetting name to valid name again should clear errors', function () {
|
||||
page.nameInput.sendKeys(testName);
|
||||
expectFormIsValid();
|
||||
});
|
||||
|
||||
it('should have enabled submit button', function () {
|
||||
const submitBtn = page.heroFormButtons.get(0);
|
||||
expect(submitBtn.isEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should hide form after submit', function () {
|
||||
page.heroFormButtons.get(0).click();
|
||||
expect(page.title.isDisplayed()).toBe(false);
|
||||
});
|
||||
|
||||
it('submitted form should be displayed', function () {
|
||||
expect(page.heroSubmitted.isElementPresent(by.css('h2'))).toBe(true);
|
||||
});
|
||||
|
||||
it('submitted form should have new hero name', function () {
|
||||
expect(page.heroSubmitted.getText()).toContain(testName);
|
||||
});
|
||||
|
||||
it('clicking edit button should reveal form again', function () {
|
||||
const editBtn = page.heroSubmitted.element(by.css('button'));
|
||||
editBtn.click();
|
||||
expect(page.heroSubmitted.isElementPresent(by.css('h2')))
|
||||
.toBe(false, 'submitted hidden again');
|
||||
expect(page.title.isDisplayed()).toBe(true, 'can see form title');
|
||||
});
|
||||
}
|
||||
|
||||
function expectFormIsValid() {
|
||||
expect(page.form.getAttribute('class')).toMatch('ng-valid');
|
||||
}
|
||||
|
||||
function expectFormIsInvalid() {
|
||||
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
|
||||
}
|
||||
|
||||
function bobTests() {
|
||||
const emsg = 'Someone named "Bob" cannot be a hero.';
|
||||
|
||||
it('should produce "no bob" error after setting name to "Bobby"', function () {
|
||||
page.nameInput.clear();
|
||||
page.nameInput.sendKeys('Bobby');
|
||||
expectFormIsInvalid();
|
||||
expect(page.errorMessages.get(0).getText()).toBe(emsg);
|
||||
});
|
||||
|
||||
it('should be ok again with valid name', function () {
|
||||
page.nameInput.clear();
|
||||
page.nameInput.sendKeys(testName);
|
||||
expectFormIsValid();
|
||||
});
|
||||
}
|
||||
|
||||
function makeNameTooLong() {
|
||||
// make the first name invalid
|
||||
page.nameInput.sendKeys('ThisHeroNameHasWayWayTooManyLetters');
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `<hero-form-template1></hero-form-template1>
|
||||
<hr>
|
||||
<hero-form-template2></hero-form-template2>
|
||||
<hr>
|
||||
<hero-form-reactive3></hero-form-reactive3>`
|
||||
})
|
||||
export class AppComponent { }
|
|
@ -0,0 +1,18 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeroFormTemplateModule } from './template/hero-form-template.module';
|
||||
import { HeroFormReactiveModule } from './reactive/hero-form-reactive.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HeroFormTemplateModule,
|
||||
HeroFormReactiveModule
|
||||
],
|
||||
declarations: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
|
@ -0,0 +1,6 @@
|
|||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -0,0 +1,47 @@
|
|||
<!-- #docregion -->
|
||||
<div class="container">
|
||||
<div [hidden]="submitted">
|
||||
<h1>Hero Form 3 (Reactive)</h1>
|
||||
<!-- #docregion form-tag-->
|
||||
<form [formGroup]="heroForm" *ngIf="active" (ngSubmit)="onSubmit()">
|
||||
<!-- #enddocregion form-tag-->
|
||||
<div class="form-group">
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<label for="name">Name</label>
|
||||
|
||||
<input type="text" id="name" class="form-control"
|
||||
formControlName="name" required >
|
||||
|
||||
<div *ngIf="formErrors.name" class="alert alert-danger">
|
||||
{{ formErrors.name }}
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input type="text" id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
<select id="power" class="form-control"
|
||||
formControlName="power" required >
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
</select>
|
||||
|
||||
<div *ngIf="formErrors.power" class="alert alert-danger">
|
||||
{{ formErrors.power }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-default"
|
||||
[disabled]="!heroForm.valid">Submit</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
(click)="addHero()">New Hero</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hero-submitted [hero]="hero" [(submitted)]="submitted"></hero-submitted>
|
||||
</div>
|
|
@ -0,0 +1,117 @@
|
|||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
|
||||
import { Hero } from '../shared/hero';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-form-reactive3',
|
||||
templateUrl: 'hero-form-reactive.component.html'
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = new Hero(18, 'Dr. WhatIsHisName', this.powers[0], 'Dr. What');
|
||||
|
||||
submitted = false;
|
||||
|
||||
// #docregion on-submit
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
this.hero = this.heroForm.value;
|
||||
}
|
||||
// #enddocregion on-submit
|
||||
// #enddocregion
|
||||
|
||||
// Reset the form with a new hero AND restore 'pristine' class state
|
||||
// by toggling 'active' flag which causes the form
|
||||
// to be removed/re-added in a tick via NgIf
|
||||
// TODO: Workaround until NgForm has a reset method (#6822)
|
||||
active = true;
|
||||
// #docregion
|
||||
// #docregion add-hero
|
||||
addHero() {
|
||||
this.hero = new Hero(42, '', '');
|
||||
this.buildForm();
|
||||
// #enddocregion add-hero
|
||||
// #enddocregion class
|
||||
|
||||
this.active = false;
|
||||
setTimeout(() => this.active = true, 0);
|
||||
// #docregion
|
||||
// #docregion add-hero
|
||||
}
|
||||
// #enddocregion add-hero
|
||||
|
||||
// #docregion form-builder
|
||||
heroForm: FormGroup;
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.buildForm();
|
||||
}
|
||||
|
||||
buildForm(): void {
|
||||
this.heroForm = this.fb.group({
|
||||
// #docregion name-validators
|
||||
'name': [this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
Validators.maxLength(24),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]
|
||||
],
|
||||
// #enddocregion name-validators
|
||||
'alterEgo': [this.hero.alterEgo],
|
||||
'power': [this.hero.power, Validators.required]
|
||||
});
|
||||
|
||||
this.heroForm.valueChanges
|
||||
.subscribe(data => this.onValueChanged(data));
|
||||
|
||||
this.onValueChanged(); // (re)set validation messages now
|
||||
}
|
||||
|
||||
// #enddocregion form-builder
|
||||
|
||||
onValueChanged(data?: any) {
|
||||
if (!this.heroForm) { return; }
|
||||
const form = this.heroForm;
|
||||
|
||||
for (const field in this.formErrors) {
|
||||
// clear previous error message (if any)
|
||||
this.formErrors[field] = '';
|
||||
const control = form.get(field);
|
||||
|
||||
if (control && control.dirty && !control.valid) {
|
||||
const messages = this.validationMessages[field];
|
||||
for (const key in control.errors) {
|
||||
this.formErrors[field] += messages[key] + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formErrors = {
|
||||
'name': '',
|
||||
'power': ''
|
||||
};
|
||||
|
||||
validationMessages = {
|
||||
'name': {
|
||||
'required': 'Name is required.',
|
||||
'minlength': 'Name must be at least 4 characters long.',
|
||||
'maxlength': 'Name cannot be more than 24 characters long.',
|
||||
'forbiddenName': 'Someone named "Bob" cannot be a hero.'
|
||||
},
|
||||
'power': {
|
||||
'required': 'Power is required.'
|
||||
}
|
||||
};
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,13 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { HeroFormReactiveComponent } from './hero-form-reactive.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ SharedModule, ReactiveFormsModule ],
|
||||
declarations: [ HeroFormReactiveComponent ],
|
||||
exports: [ HeroFormReactiveComponent ]
|
||||
})
|
||||
export class HeroFormReactiveModule { }
|
|
@ -0,0 +1,43 @@
|
|||
// #docregion
|
||||
import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';
|
||||
|
||||
// #docregion custom-validator
|
||||
/** A hero's name can't match the given regular expression */
|
||||
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
const name = control.value;
|
||||
const no = nameRe.test(name);
|
||||
return no ? {'forbiddenName': {name}} : null;
|
||||
};
|
||||
}
|
||||
// #enddocregion custom-validator
|
||||
|
||||
// #docregion directive
|
||||
@Directive({
|
||||
selector: '[forbiddenName]',
|
||||
// #docregion directive-providers
|
||||
providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
|
||||
// #enddocregion directive-providers
|
||||
})
|
||||
export class ForbiddenValidatorDirective implements Validator, OnChanges {
|
||||
@Input() forbiddenName: string;
|
||||
private valFn = Validators.nullValidator;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const change = changes['forbiddenName'];
|
||||
if (change) {
|
||||
const val: string | RegExp = change.currentValue;
|
||||
const re = val instanceof RegExp ? val : new RegExp(val, 'i');
|
||||
this.valFn = forbiddenNameValidator(re);
|
||||
} else {
|
||||
this.valFn = Validators.nullValidator;
|
||||
}
|
||||
}
|
||||
|
||||
validate(control: AbstractControl): {[key: string]: any} {
|
||||
return this.valFn(control);
|
||||
}
|
||||
}
|
||||
// #docregion directive
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
// #docregion
|
||||
export class Hero {
|
||||
|
||||
constructor(
|
||||
public id: number,
|
||||
public name: string,
|
||||
public power: string,
|
||||
public alterEgo?: string
|
||||
) { }
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { ForbiddenValidatorDirective } from './forbidden-name.directive';
|
||||
import { SubmittedComponent } from './submitted.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule],
|
||||
declarations: [ ForbiddenValidatorDirective, SubmittedComponent ],
|
||||
exports: [ ForbiddenValidatorDirective, SubmittedComponent,
|
||||
CommonModule ]
|
||||
})
|
||||
export class SharedModule { }
|
|
@ -0,0 +1,32 @@
|
|||
// #docregion
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-submitted',
|
||||
template: `
|
||||
<div *ngIf="submitted">
|
||||
<h2>You submitted the following:</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Name</div>
|
||||
<div class="col-xs-9 pull-left">{{ hero.name }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Alter Ego</div>
|
||||
<div class="col-xs-9 pull-left">{{ hero.alterEgo }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Power</div>
|
||||
<div class="col-xs-9 pull-left">{{ hero.power }}</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-default" (click)="onClick()">Edit</button>
|
||||
</div>`
|
||||
})
|
||||
export class SubmittedComponent {
|
||||
@Input() hero: Hero;
|
||||
@Input() submitted = false;
|
||||
@Output() submittedChange = new EventEmitter<boolean>();
|
||||
onClick() { this.submittedChange.emit(false); }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { HeroFormTemplate1Component } from './hero-form-template1.component';
|
||||
import { HeroFormTemplate2Component } from './hero-form-template2.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ SharedModule, FormsModule ],
|
||||
declarations: [ HeroFormTemplate1Component, HeroFormTemplate2Component ],
|
||||
exports: [ HeroFormTemplate1Component, HeroFormTemplate2Component ]
|
||||
})
|
||||
export class HeroFormTemplateModule { }
|
|
@ -0,0 +1,61 @@
|
|||
<!-- #docregion -->
|
||||
<div class="container">
|
||||
<div [hidden]="submitted">
|
||||
<h1>Hero Form 1 (Template)</h1>
|
||||
<!-- #docregion form-tag-->
|
||||
<form #heroForm="ngForm" *ngIf="active" (ngSubmit)="onSubmit()">
|
||||
<!-- #enddocregion form-tag-->
|
||||
<div class="form-group">
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<label for="name">Name</label>
|
||||
|
||||
<input type="text" id="name" class="form-control"
|
||||
required minlength="4" maxlength="24"
|
||||
name="name" [(ngModel)]="hero.name"
|
||||
#name="ngModel" >
|
||||
|
||||
<div *ngIf="name.errors && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
<div [hidden]="!name.errors.required">
|
||||
Name is required
|
||||
</div>
|
||||
<div [hidden]="!name.errors.minlength">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div [hidden]="!name.errors.maxlength">
|
||||
Name cannot be more than 24 characters long.
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input type="text" id="alterEgo" class="form-control"
|
||||
name="alterEgo"
|
||||
[(ngModel)]="hero.alterEgo" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
<select id="power" class="form-control"
|
||||
name="power"
|
||||
[(ngModel)]="hero.power" required
|
||||
#power="ngModel" >
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
</select>
|
||||
|
||||
<div *ngIf="power.errors && power.touched" class="alert alert-danger">
|
||||
<div [hidden]="!power.errors.required">Power is required</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-default"
|
||||
[disabled]="!heroForm.form.valid">Submit</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
(click)="addHero()">New Hero</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hero-submitted [hero]="hero" [(submitted)]="submitted"></hero-submitted>
|
||||
</div>
|
|
@ -0,0 +1,48 @@
|
|||
/* tslint:disable: member-ordering */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
|
||||
import { Hero } from '../shared/hero';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-form-template1',
|
||||
templateUrl: 'hero-form-template1.component.html'
|
||||
})
|
||||
// #docregion class
|
||||
export class HeroFormTemplate1Component {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');
|
||||
|
||||
submitted = false;
|
||||
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
}
|
||||
// #enddocregion class
|
||||
// #enddocregion
|
||||
// Reset the form with a new hero AND restore 'pristine' class state
|
||||
// by toggling 'active' flag which causes the form
|
||||
// to be removed/re-added in a tick via NgIf
|
||||
// TODO: Workaround until NgForm has a reset method (#6822)
|
||||
active = true;
|
||||
// #docregion
|
||||
// #docregion class
|
||||
|
||||
addHero() {
|
||||
this.hero = new Hero(42, '', '');
|
||||
// #enddocregion class
|
||||
// #enddocregion
|
||||
|
||||
this.active = false;
|
||||
setTimeout(() => this.active = true, 0);
|
||||
// #docregion
|
||||
// #docregion class
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
// #enddocregion
|
|
@ -0,0 +1,52 @@
|
|||
<!-- #docregion -->
|
||||
<div class="container">
|
||||
<div [hidden]="submitted">
|
||||
<h1>Hero Form 2 (Template & Messages)</h1>
|
||||
<!-- #docregion form-tag-->
|
||||
<form #heroForm="ngForm" *ngIf="active" (ngSubmit)="onSubmit()">
|
||||
<!-- #enddocregion form-tag-->
|
||||
<div class="form-group">
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<label for="name">Name</label>
|
||||
|
||||
<!-- #docregion name-input -->
|
||||
<input type="text" id="name" class="form-control"
|
||||
required minlength="4" maxlength="24" forbiddenName="bob"
|
||||
name="name" [(ngModel)]="hero.name" >
|
||||
<!-- #enddocregion name-input -->
|
||||
|
||||
<div *ngIf="formErrors.name" class="alert alert-danger">
|
||||
{{ formErrors.name }}
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input type="text" id="alterEgo" class="form-control"
|
||||
name="alterEgo"
|
||||
[(ngModel)]="hero.alterEgo" >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
<select id="power" class="form-control"
|
||||
name="power"
|
||||
[(ngModel)]="hero.power" required >
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
</select>
|
||||
|
||||
<div *ngIf="formErrors.power" class="alert alert-danger">
|
||||
{{ formErrors.power }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-default"
|
||||
[disabled]="!heroForm.form.valid">Submit</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
(click)="addHero()">New Hero</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hero-submitted [hero]="hero" [(submitted)]="submitted"></hero-submitted>
|
||||
</div>
|
|
@ -0,0 +1,100 @@
|
|||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, AfterViewChecked, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { Hero } from '../shared/hero';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-form-template2',
|
||||
templateUrl: 'hero-form-template2.component.html'
|
||||
})
|
||||
export class HeroFormTemplate2Component implements AfterViewChecked {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');
|
||||
|
||||
submitted = false;
|
||||
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
// Reset the form with a new hero AND restore 'pristine' class state
|
||||
// by toggling 'active' flag which causes the form
|
||||
// to be removed/re-added in a tick via NgIf
|
||||
// TODO: Workaround until NgForm has a reset method (#6822)
|
||||
active = true;
|
||||
// #docregion
|
||||
|
||||
addHero() {
|
||||
this.hero = new Hero(42, '', '');
|
||||
// #enddocregion
|
||||
|
||||
this.active = false;
|
||||
setTimeout(() => this.active = true, 0);
|
||||
// #docregion
|
||||
}
|
||||
|
||||
// #docregion view-child
|
||||
heroForm: NgForm;
|
||||
@ViewChild('heroForm') currentForm: NgForm;
|
||||
|
||||
ngAfterViewChecked() {
|
||||
this.formChanged();
|
||||
}
|
||||
|
||||
formChanged() {
|
||||
if (this.currentForm === this.heroForm) { return; }
|
||||
this.heroForm = this.currentForm;
|
||||
if (this.heroForm) {
|
||||
this.heroForm.valueChanges
|
||||
.subscribe(data => this.onValueChanged(data));
|
||||
}
|
||||
}
|
||||
// #enddocregion view-child
|
||||
|
||||
// #docregion handler
|
||||
onValueChanged(data?: any) {
|
||||
if (!this.heroForm) { return; }
|
||||
const form = this.heroForm.form;
|
||||
|
||||
for (const field in this.formErrors) {
|
||||
// clear previous error message (if any)
|
||||
this.formErrors[field] = '';
|
||||
const control = form.get(field);
|
||||
|
||||
if (control && control.dirty && !control.valid) {
|
||||
const messages = this.validationMessages[field];
|
||||
for (const key in control.errors) {
|
||||
this.formErrors[field] += messages[key] + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formErrors = {
|
||||
'name': '',
|
||||
'power': ''
|
||||
};
|
||||
// #enddocregion handler
|
||||
|
||||
// #docregion messages
|
||||
validationMessages = {
|
||||
'name': {
|
||||
'required': 'Name is required.',
|
||||
'minlength': 'Name must be at least 4 characters long.',
|
||||
'maxlength': 'Name cannot be more than 24 characters long.',
|
||||
'forbiddenName': 'Someone named "Bob" cannot be a hero.'
|
||||
},
|
||||
'power': {
|
||||
'required': 'Power is required.'
|
||||
}
|
||||
};
|
||||
// #enddocregion messages
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,9 +1,7 @@
|
|||
/* #docregion */
|
||||
.ng-valid[required] {
|
||||
.ng-valid[required], .ng-valid.required {
|
||||
border-left: 5px solid #42A948; /* green */
|
||||
}
|
||||
|
||||
.ng-invalid {
|
||||
.ng-invalid:not(form) {
|
||||
border-left: 5px solid #a94442; /* red */
|
||||
}
|
||||
/* #enddocregion */
|
|
@ -1,18 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Hero Form</title>
|
||||
<title>Hero Form with Validation</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<!-- #docregion bootstrap -->
|
||||
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
|
||||
<!-- #enddocregion bootstrap -->
|
||||
<!-- #docregion styles -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="forms.css">
|
||||
<!-- #enddocregion styles -->
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
@ -30,5 +25,5 @@
|
|||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Forms-Deprecated",
|
||||
"description": "Validation",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
|
@ -1,6 +1,6 @@
|
|||
// #docregion
|
||||
import { browserDynamicPlatform } from '@angular/platform-browser-dynamic';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
browserDynamicPlatform().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Set The Document Title In Angular 2",
|
||||
"description": "Set The Document Title In Angular",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('TypeScript to Javascript tests', function () {
|
|||
it('should support optional, attribute, and query injections', function () {
|
||||
let app = element(by.css('hero-di-inject-additional'));
|
||||
let h1 = app.element(by.css('h1'));
|
||||
let okMsg = app.element(by.css('.ok-msg'));
|
||||
let okMsg = app.element(by.css('p'));
|
||||
|
||||
expect(h1.getText()).toBe('Tour of Heroes');
|
||||
app.element(by.buttonText('OK')).click();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
template:
|
||||
'<h1>{{titlePrefix}} {{title}}</h1>' +
|
||||
'<button (click)="ok()">OK</button>' +
|
||||
'<ng-content></ng-content>'
|
||||
'<p>{{ msg }}</p>'
|
||||
}).Class({
|
||||
constructor: [
|
||||
[
|
||||
|
@ -14,20 +14,14 @@
|
|||
new ng.core.Inject('titlePrefix')
|
||||
],
|
||||
new ng.core.Attribute('title'),
|
||||
[
|
||||
new ng.core.Query('okMsg'),
|
||||
ng.core.ElementRef
|
||||
],
|
||||
function(titlePrefix, title, msg) {
|
||||
function(titlePrefix, title) {
|
||||
this.titlePrefix = titlePrefix;
|
||||
this.title = title;
|
||||
this.msg = msg;
|
||||
this.msg = '';
|
||||
}
|
||||
],
|
||||
ok: function() {
|
||||
var msgEl =
|
||||
this.msg.first.nativeElement;
|
||||
msgEl.textContent = 'OK!';
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
|
@ -35,7 +29,6 @@
|
|||
var AppComponent = ng.core.Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: '<hero-title title="Tour of Heroes">' +
|
||||
'<span #okMsg class="ok-msg"></span>' +
|
||||
'</hero-title>'
|
||||
}).Class({
|
||||
constructor: function() { }
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
// #enddocregion appimport
|
||||
|
||||
// #docregion ng2import
|
||||
var bootstrap =
|
||||
ng.platformBrowserDynamic.bootstrap;
|
||||
var platformBrowserDynamic =
|
||||
ng.platformBrowserDynamic.platformBrowserDynamic;
|
||||
var LocationStrategy =
|
||||
ng.common.LocationStrategy;
|
||||
var HashLocationStrategy =
|
||||
|
@ -17,20 +17,17 @@
|
|||
// #enddocregion appimport
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic();
|
||||
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesDslModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesLifecycleModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesDIModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroDIInlineModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectModule2);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectAdditionalModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesIOModule);
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesHostBindingsModule);
|
||||
|
||||
platformBrowserDynamic.bootstrapModule(app.HeroesQueriesModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesDslModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesLifecycleModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesDIModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroDIInlineModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesDIInjectModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesDIInjectModule2);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesDIInjectAdditionalModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesIOModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesHostBindingsModule);
|
||||
platformBrowserDynamic().bootstrapModule(app.HeroesQueriesModule);
|
||||
});
|
||||
|
||||
// #docregion appimport
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
|
||||
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
|
||||
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||
<script src="node_modules/@angular/core/bundles/core.umd.js"></script>
|
||||
<script src="node_modules/@angular/common/bundles/common.umd.js"></script>
|
||||
<script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import {
|
||||
Attribute,
|
||||
Component,
|
||||
ElementRef,
|
||||
Inject,
|
||||
Optional,
|
||||
Query,
|
||||
QueryList,
|
||||
NgModule
|
||||
} from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
@ -16,24 +13,21 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||
template: `
|
||||
<h1>{{titlePrefix}} {{title}}</h1>
|
||||
<button (click)="ok()">OK</button>
|
||||
<ng-content></ng-content>
|
||||
<p>{{ msg }}</p>
|
||||
`
|
||||
})
|
||||
class TitleComponent {
|
||||
private msg: string = '';
|
||||
constructor(
|
||||
@Inject('titlePrefix')
|
||||
@Optional()
|
||||
private titlePrefix: string,
|
||||
@Attribute('title')
|
||||
private title: string,
|
||||
@Query('okMsg')
|
||||
private msg: QueryList<ElementRef>) {
|
||||
private title: string) {
|
||||
}
|
||||
|
||||
ok() {
|
||||
let msgEl =
|
||||
this.msg.first.nativeElement;
|
||||
msgEl.textContent = 'OK!';
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -41,7 +35,6 @@ class TitleComponent {
|
|||
@Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: `<hero-title title="Tour of Heroes">
|
||||
<span #okMsg class="ok-msg"></span>
|
||||
</hero-title>`
|
||||
})
|
||||
class AppComponent { }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* tslint:disable no-unused-variable */
|
||||
// #docregion ng2import
|
||||
import { bootstrap }
|
||||
from '@angular/platform-browser-dynamic';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import {
|
||||
LocationStrategy,
|
||||
HashLocationStrategy
|
||||
|
@ -12,8 +11,6 @@ import {
|
|||
import { HeroComponent } from './hero.component';
|
||||
// #enddocregion appimport
|
||||
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { HeroesModule } from './hero.component';
|
||||
import { HeroesLifecycleModule } from './hero-lifecycle.component';
|
||||
import { HeroesDIModule } from './hero-di.component';
|
||||
|
|
|
@ -7,6 +7,6 @@ describe('cli-quickstart App', () => {
|
|||
|
||||
it('should display message saying app works', () => {
|
||||
let pageTitle = element(by.css('cli-quickstart-app h1')).getText();
|
||||
expect(pageTitle).toEqual('My First Angular 2 App');
|
||||
expect(pageTitle).toEqual('My First Angular App');
|
||||
});
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue