Merge remote-tracking branch 'origin/master'

# Conflicts:
#	README.md
#	public/_includes/_scripts-include.jade
#	public/docs/_examples/homepage-hello-world/ts/index.1.html
#	public/docs/_examples/homepage-tabs/ts/index.1.html
#	public/docs/_examples/homepage-todo/ts/index.1.html
#	public/docs/_examples/systemjs.config.plunker.build.js
#	public/docs/_examples/systemjs.config.plunker.js
#	public/docs/js/latest/cookbook/ts-to-js.jade
#	public/docs/ts/latest/_data.json
#	public/docs/ts/latest/cookbook/component-communication.jade
#	public/docs/ts/latest/cookbook/component-relative-paths.jade
#	public/docs/ts/latest/glossary.jade
#	public/docs/ts/latest/guide/_data.json
#	public/docs/ts/latest/guide/architecture.jade
#	public/docs/ts/latest/guide/attribute-directives.jade
#	public/docs/ts/latest/guide/dependency-injection.jade
#	public/docs/ts/latest/guide/index.jade
#	public/docs/ts/latest/guide/lifecycle-hooks.jade
#	public/docs/ts/latest/guide/ngmodule.jade
#	public/docs/ts/latest/guide/npm-packages.jade
#	public/docs/ts/latest/guide/security.jade
#	public/docs/ts/latest/guide/server-communication.jade
#	public/docs/ts/latest/guide/template-syntax.jade
#	public/docs/ts/latest/guide/typescript-configuration.jade
#	public/docs/ts/latest/index.jade
#	public/docs/ts/latest/quickstart.jade
#	public/docs/ts/latest/tutorial/_data.json
#	public/docs/ts/latest/tutorial/toh-pt6.jade
#	public/features.jade
#	public/resources/js/directives/live-example.js
#	tools/plunker-builder/indexHtmlTranslator.js
This commit is contained in:
Zhicheng Wang 2016-09-08 17:20:01 +08:00
commit 969da7d321
420 changed files with 5766 additions and 4388 deletions

4
.gitignore vendored
View File

@ -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

View File

@ -12,18 +12,17 @@ env:
- CHROME_BIN=chromium-browser
- LATEST_RELEASE=2.0.0-rc.5
- 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 +33,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

View File

@ -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
@ -118,22 +120,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);
@ -346,6 +361,7 @@ function runE2eDartTests(appDir, outputFile) {
}
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) {
@ -508,23 +524,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
@ -598,8 +633,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 });
});
gulp.task('build-dart-cheatsheet', [], function() {
@ -608,8 +645,8 @@ gulp.task('build-dart-cheatsheet', [], function() {
gulp.task('dartdoc', ['pub upgrade'], function() {
const ngRepoPath = ngPathFor('dart');
if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, 'docs', 'api'))) {
gutil.log('Skipping dartdoc: --fast flag enabled and "docs/api" 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);
@ -618,7 +655,7 @@ gulp.task('dartdoc', ['pub upgrade'], function() {
renameIfExistsSync(topLevelLibFilePath, tmpPath);
gutil.log(`Hiding top-level angular2 library: ${topLevelLibFilePath}`);
// Remove dartdoc '--add-crossdart' flag while we are fixing links to API pages.
const dartdoc = spawnExt('dartdoc', ['--output', 'docs/api'], { cwd: ngRepoPath});
const dartdoc = spawnExt('dartdoc', ['--output', relDartDocApiDir], { cwd: ngRepoPath});
return dartdoc.promise.finally(() => {
gutil.log(`Restoring top-level angular2 library: ${topLevelLibFilePath}`);
renameIfExistsSync(tmpPath, topLevelLibFilePath);
@ -682,12 +719,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}`);
@ -695,7 +732,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');
});
@ -704,13 +741,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');
@ -806,7 +836,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;
@ -856,26 +886,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;
@ -974,6 +1018,52 @@ 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);
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
@ -1073,11 +1163,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);
@ -1092,11 +1187,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'",
@ -1145,8 +1239,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) {
@ -1162,8 +1257,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) {
@ -1246,7 +1342,7 @@ function buildApiDocsForDart() {
log.level = _dgeniLogLevel;
const dabInfo = dab.dartPkgConfigInfo;
dabInfo.ngIoDartApiDocPath = path.join(DOCS_PATH, 'dart', vers, 'api');
dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), 'docs', '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$/);
@ -1461,8 +1557,9 @@ function checkAngularProjectPath(_ngPath) {
function renameIfExistsSync(oldPath, newPath) {
if (fs.existsSync(oldPath)) {
fs.renameSync(oldPath, newPath);
gutil.log(`Rename: mv ${oldPath} ${newPath}`);
fs.renameSync(oldPath, newPath);
} else {
gutil.log(`renameIfExistsSync cannot find file to rename: ${oldPath}`);
gutil.log(`renameIfExistsSync cannot rename, path not found: ${oldPath}`);
}
}

View File

@ -71,7 +71,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": {

View File

@ -35,5 +35,8 @@ header(class="hero background-sky", style=fixHeroCss ? "height:auto" : "")
else if current.path[3] == 'api' && current.path[1] == 'dart'
block breadcrumbs
//- Show cross-language menu for top-level API page (but not entry pages)
if ! public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]]
!= partial("_version-dropdown")
else if current.path[0] == "docs"
!= partial("_version-dropdown")

View File

@ -155,64 +155,66 @@ mixin makeJson( filePath, jsonConfig, title, stylePatterns)
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')
@ -299,7 +301,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;

View File

@ -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,7 +33,7 @@ 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
@ -42,7 +42,7 @@ mixin tree(directory, urlPrefix, name, latest)
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'
@ -72,4 +72,6 @@ nav.dropdown
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")
//- 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 2 for Dart")

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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');
}
}

View File

@ -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

View File

@ -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

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

View File

@ -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 { }

View File

@ -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);

View File

@ -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 {}

View File

@ -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 ]
})

View File

@ -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'; }

View File

@ -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');
}

View File

@ -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 { }

View File

@ -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 { }

View File

@ -0,0 +1,6 @@
// #docregion
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -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>

View File

@ -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

View File

@ -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 { }

View File

@ -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

View File

@ -0,0 +1,9 @@
// #docregion
export class Hero {
constructor(
public id: number,
public name: string,
public power: string,
public alterEgo?: string
) { }
}

View File

@ -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 { }

View File

@ -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); }
}

View File

@ -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 { }

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -0,0 +1,7 @@
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}

View File

@ -0,0 +1,29 @@
<html>
<head>
<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">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="forms.css">
<!-- Polyfill(s) for older browsers -->
<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>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>

View File

@ -0,0 +1,7 @@
{
"description": "Validation",
"files":[
"!**/*.d.ts",
"!**/*.js"
]
}

View File

@ -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);

View File

@ -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();

View File

@ -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() { }

View File

@ -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

View File

@ -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 { }

View File

@ -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';

View File

@ -7,9 +7,21 @@ import { HeroesComponent } from './heroes/heroes.component';
import { HeroListComponent } from './heroes/hero-list.component';
import { InjectorComponent } from './injector.component';
import { TestComponent } from './test.component';
import { ProvidersComponent } from './providers.component';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
import { UserService } from './user.service';
import {
ProvidersComponent,
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
} from './providers.component';
// #docregion ngmodule
@NgModule({
@ -22,7 +34,18 @@ import { UserService } from './user.service';
HeroesComponent,
HeroListComponent,
InjectorComponent,
TestComponent
TestComponent,
ProvidersComponent,
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
],
// #docregion ngmodule-providers
providers: [

View File

@ -1,6 +1,6 @@
import { browserDynamicPlatform } from '@angular/platform-browser-dynamic';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
// #docregion bootstrap
browserDynamicPlatform().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule);
// #enddocregion bootstrap

View File

@ -261,18 +261,6 @@ export class Provider10Component implements OnInit {
<div id="p8"><provider-8></provider-8></div>
<div id="p9"><provider-9></provider-9></div>
<div id="p10"><provider-10></provider-10></div>
`,
directives: [
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
],
`
})
export class ProvidersComponent { }

View File

@ -1,18 +0,0 @@
/* tslint:disable */
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppCtorComponent } from './app-ctor.component';
import { AppComponent as v1 } from './app.component.1';
import { AppComponent as v2 } from './app.component.2';
import { AppComponent as v3 } from './app.component.3';
import { AppComponent as final } from './app.component';
// pick one
// bootstrap(v1);
// bootstrap(v2);
// bootstrap(v3);
bootstrap(final);
// for doc testing
bootstrap(AppCtorComponent);

View File

@ -1,9 +1,9 @@
/* #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 */
/* #enddocregion */

View File

@ -11,7 +11,7 @@
<!-- Polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.12"></script>
<script src="https://unpkg.com/zone.js@0.6.17"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>

View File

@ -0,0 +1,17 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DiDemoComponent } from './di_demo';
import { UiTabsComponent, UiPaneDirective } from './ui_tabs';
@NgModule({
imports: [ BrowserModule ],
declarations: [
DiDemoComponent,
UiTabsComponent,
UiPaneDirective
],
bootstrap: [ DiDemoComponent ]
})
export class AppModule { }

View File

@ -1,8 +1,6 @@
// #docregion
import { Component } from '@angular/core';
import { UiTabsComponent, UiPaneDirective } from './ui_tabs';
class Detail {
title: string;
text: string;
@ -26,8 +24,7 @@ class Detail {
</ui-tabs>
<hr>
<button class="btn" (click)="addDetail()">Add Detail</button>
`,
directives: [UiTabsComponent, UiPaneDirective]
`
})
export class DiDemoComponent {
details: Detail[] = [];

View File

@ -1,6 +1,6 @@
// #docregion
import { bootstrap } from '@angular/platform-browser-dynamic';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { DiDemoComponent } from './di_demo';
import { AppModule } from './app.module';
bootstrap(DiDemoComponent);
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -12,7 +12,7 @@
<!-- Polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.12"></script>
<script src="https://unpkg.com/zone.js@0.6.17"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>

View File

@ -4,13 +4,19 @@ import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { TodoAppComponent } from './todo_app';
import { TodoListComponent } from './todo_list';
import { TodoFormComponent } from './todo_form';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [ TodoAppComponent ],
declarations: [
TodoAppComponent,
TodoListComponent,
TodoFormComponent
],
bootstrap: [ TodoAppComponent ]
})
export class AppModule { }

View File

@ -2,8 +2,6 @@
import { Component } from '@angular/core';
import { Todo } from './todo';
import { TodoListComponent } from './todo_list';
import { TodoFormComponent } from './todo_form';
@Component({
selector: 'todo-app',
@ -14,8 +12,7 @@ import { TodoFormComponent } from './todo_form';
<todo-list [todos]="todos"></todo-list>
<todo-form (newTask)="addTask($event)"></todo-form>`,
styles: ['a { cursor: pointer; cursor: hand; }'],
directives: [TodoListComponent, TodoFormComponent]
styles: ['a { cursor: pointer; cursor: hand; }']
})
export class TodoAppComponent {
todos: Todo[] = [

View File

@ -12,7 +12,7 @@
<!-- Polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.12"></script>
<script src="https://unpkg.com/zone.js@0.6.17"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>

View File

@ -28,22 +28,18 @@ System.config({
});
System.import('systemjs.config.js')
.then(function () {
return Promise.all([
.then(() => Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing')
])
]))
.then((providers) => {
var coreTesting = providers[0];
var browserTesting = providers[1];
coreTesting.TestBed.initTestEnvironment(
browserTesting.BrowserDynamicTestingModule,
browserTesting.platformBrowserDynamicTesting());
})
.then(function (providers) {
var testing = providers[0];
var testingBrowser = providers[1];
testing.setBaseTestProviders(
testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
})
.then(function() {
.then(function () {
// Finally, load all spec files.
// This will run the tests directly.
return Promise.all(

View File

@ -66,8 +66,8 @@ describe('Lifecycle hooks', function () {
expect(titleEle.getText()).toContain('Windstorm can sing');
changeLogEles.count().then(function(count) {
// Empirically 5 messages to start
expect(count).toBeGreaterThan(4, 'should start with some messages');
// 3 messages to start
expect(count).toEqual(3, 'should start with 3 messages');
logCount = count;
// heroNameInputEle.sendKeys('-foo-').then(function () {
return sendKeys(heroNameInputEle, '-foo-');
@ -82,8 +82,7 @@ describe('Lifecycle hooks', function () {
return sendKeys(powerInputEle, '-bar-');
}).then(function () {
expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-');
// 7 == 2 previously + length of '-bar-'
expect(changeLogEles.count()).toEqual(logCount + 11, 'should add 11 more messages');
expect(changeLogEles.count()).toEqual(logCount + 6, 'should add 6 more messages');
});
});

View File

@ -1,6 +1,6 @@
/* tslint:disable:forin */
// #docregion
import { Component, DoCheck, Input, OnChanges, SimpleChange, ViewChild } from '@angular/core';
import { Component, DoCheck, Input, ViewChild } from '@angular/core';
class Hero {
constructor(public name: string) {}
@ -21,7 +21,7 @@ class Hero {
'p {background: Yellow; padding: 8px; margin-top: 8px}'
]
})
export class DoCheckComponent implements DoCheck, OnChanges {
export class DoCheckComponent implements DoCheck {
@Input() hero: Hero;
@Input() power: string;
@ -66,16 +66,6 @@ export class DoCheckComponent implements DoCheck, OnChanges {
}
// #enddocregion ng-do-check
// Copied from OnChangesComponent
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
this.changeLog.push(`OnChanges: ${propName}: currentValue = ${cur}, previousValue = ${prev}`);
}
}
reset() {
this.changeDetected = true;
this.changeLog.length = 0;

View File

@ -6,6 +6,7 @@ describe('NgModule', function () {
const gold = 'rgba(255, 215, 0, 1)';
const powderblue = 'rgba(176, 224, 230, 1)';
const lightgray = 'rgba(211, 211, 211, 1)';
const white = 'rgba(0, 0, 0, 0)';
function getCommonsSectionStruct() {
const buttons = element.all(by.css('nav a'));
@ -55,7 +56,7 @@ describe('NgModule', function () {
}
// tests
function appTitleTests(color: string) {
function appTitleTests(color: string, name?: string) {
return function() {
it('should have a gray header', function() {
const commons = getCommonsSectionStruct();
@ -64,16 +65,16 @@ describe('NgModule', function () {
it('should welcome us', function () {
const commons = getCommonsSectionStruct();
expect(commons.subtitle.getText()).toBe('Welcome, Sam Spade');
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
});
};
}
function contactTests(color: string) {
function contactTests(color: string, name?: string) {
return function() {
it('shows the contact\'s owner', function() {
const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe('Contact of Sam Spade');
expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
});
it('can cycle between contacts', function () {
@ -114,9 +115,9 @@ describe('NgModule', function () {
browser.get('');
});
describe('app-title', appTitleTests(lightgray));
describe('app-title', appTitleTests(white, 'Miss Marple'));
describe('contact', contactTests(lightgray));
describe('contact', contactTests(lightgray, 'Miss Marple'));
describe('crisis center', function () {
beforeEach(function () {
@ -149,7 +150,7 @@ describe('NgModule', function () {
it('shows a list of heroes', function() {
const heroes = getHeroesSectionStruct();
expect(heroes.header.getText()).toBe('Heroes of Sam Spade');
expect(heroes.header.getText()).toBe('Heroes of Miss Marple');
expect(heroes.title.getText()).toBe('Hero List');
expect(heroes.items.count()).toBe(6);
expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice');

View File

@ -11,8 +11,6 @@ import { UserService } from './user.service';
/* Feature Modules */
import { ContactModule } from './contact/contact.module.3';
import { routing } from './app.routing.3';
@NgModule({
@ -23,9 +21,8 @@ import { routing } from './app.routing.3';
routing
],
// #enddocregion imports
declarations: [ AppComponent, HighlightDirective, TitleComponent ],
providers: [ UserService ],
declarations: [ AppComponent, HighlightDirective, TitleComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,29 +1,40 @@
// #docplaster
// #docregion
// #docregion v4
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Root */
import { AppComponent } from './app.component';
/* Feature Modules */
import { ContactModule } from './contact/contact.module';
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core/core.module';
import { routing } from './app.routing';
@NgModule({
// #docregion import-for-root
imports: [
BrowserModule,
ContactModule,
routing,
SharedModule.forRoot()
// #enddocregion v4
// #enddocregion
// #enddocregion import-for-root
/*
// #docregion v4
CoreModule,
// #enddocregion v4
*/
// #docregion import-for-root
// #docregion
CoreModule.forRoot({userName: 'Miss Marple'}),
// #docregion v4
routing
],
// #enddocregion import-for-root
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
// #enddocregion v4
// #enddocregion

View File

@ -1,11 +1,10 @@
import { ModuleWithProviders } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module' },
{ path: 'heroes', loadChildren: 'app/hero/hero.module.3' }
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: 'app/hero/hero.module.3#HeroModule' }
];
export const routing: ModuleWithProviders = RouterModule.forRoot(routes);

View File

@ -1,13 +1,12 @@
// #docregion
import { ModuleWithProviders } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
// #docregion lazy-routes
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module' },
{ path: 'heroes', loadChildren: 'app/hero/hero.module' }
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule' }
// #enddocregion lazy-routes
];

View File

@ -1,9 +1,9 @@
// Exact copy except import UserService from shared
// Exact copy except import UserService from core
// #docregion
import { Component, OnInit } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../shared/user.service';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-contact',

View File

@ -15,7 +15,6 @@ import { routing } from './contact.routing.3';
@NgModule({
imports: [ CommonModule, FormsModule, routing ],
declarations: [ ContactComponent, HighlightDirective, AwesomePipe ],
providers: [ ContactService ]
})
export class ContactModule { }

View File

@ -1,7 +1,7 @@
import { ModuleWithProviders } from '@angular/core';
import { RouterModule } from '@angular/router';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component.3';
import { ContactComponent } from './contact.component.3';
export const routing: ModuleWithProviders = RouterModule.forChild([
{ path: 'contact', component: ContactComponent}

View File

@ -1,7 +1,7 @@
import { ModuleWithProviders } from '@angular/core';
import { RouterModule } from '@angular/router';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component';
import { ContactComponent } from './contact.component';
// #docregion routing
export const routing: ModuleWithProviders = RouterModule.forChild([

View File

@ -0,0 +1,48 @@
/* tslint:disable:member-ordering no-unused-variable */
// #docplaster
// #docregion
// #docregion v4
import {
ModuleWithProviders, NgModule,
Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
// #enddocregion
import { UserServiceConfig } from './user.service';
// #docregion v4
@NgModule({
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
providers: [ UserService ]
})
export class CoreModule {
// #enddocregion v4
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
// #docregion v4
}
// #enddocregion v4
// #enddocregion

View File

@ -1,4 +1,4 @@
<!-- Exact copy from app.component.html -->
<!-- Exact copy from earlier app.component.html -->
<h1 highlight>{{title}} {{subtitle}}</h1>
<p *ngIf="user">
<i>Welcome, {{user}}</i>

View File

@ -1,10 +1,10 @@
// Exact copy of app/title.component.ts except import UserService from shared
import { Component, Input } from '@angular/core';
import { UserService } from './user.service';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-title',
templateUrl: 'app/shared/title.component.html',
templateUrl: 'app/core/title.component.html',
})
export class TitleComponent {
@Input() subtitle = '';

View File

@ -0,0 +1,32 @@
// Crazy copy of the app/user.service
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable, Optional } from '@angular/core';
let nextId = 1;
export class UserServiceConfig {
userName = 'Philip Marlowe';
}
@Injectable()
export class UserService {
id = nextId++;
private _userName = 'Sherlock Holmes';
// #docregion ctor
constructor(@Optional() config: UserServiceConfig) {
if (config) { this._userName = config.userName; }
}
// #enddocregion ctor
get userName() {
// Demo: add a suffix if this service has been created more than once
const suffix = this.id > 1 ? ` times ${this.id}` : '';
return this._userName + suffix;
}
}

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
template: `

View File

@ -11,6 +11,4 @@ import { routing } from './crisis.routing';
declarations: [ CrisisDetailComponent, CrisisListComponent ],
providers: [ CrisisService ]
})
// #docregion export-default
export default class CrisisModule {}
// #enddocregion export-default
export class CrisisModule {}

View File

@ -1,6 +1,6 @@
import { ModuleWithProviders } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
RouterModule } from '@angular/router';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';

View File

@ -1,8 +1,8 @@
// Exact copy except import UserService from shared
// Exact copy except import UserService from core
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { UserService } from '../shared/user.service';
import { UserService } from '../core/user.service';
@Component({
template: `

View File

@ -1,20 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HeroComponent } from './hero.component.3';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HighlightDirective } from './highlight.directive';
import { routing } from './hero.routing.3';
import { HeroComponent } from './hero.component.3';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HighlightDirective } from './highlight.directive';
import { routing } from './hero.routing.3';
// TODO: Remove in RC 6
import { HeroService } from './hero.service';
// #docregion class
@NgModule({
imports: [ CommonModule, FormsModule, routing ],
// TODO: Remove in RC 6
providers: [ HeroService ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
HighlightDirective
]
})
export default class HeroModule { }
export class HeroModule { }
// #enddocregion class

View File

@ -1,23 +1,21 @@
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { SharedModule } from '../shared/shared.module';
import { HeroComponent } from './hero.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { routing } from './hero.routing';
import { HeroComponent } from './hero.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { routing } from './hero.routing';
/*
* TODO: Remove THE HeroService class and provider after
* https://github.com/angular/angular/pull/10579 lands
*/
// TODO: Remove THE HeroService class in RC 6
import { HeroService } from './hero.service';
@NgModule({
imports: [ SharedModule, routing ],
// TODO: Remove in RC 6
providers: [ HeroService ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
]
})
export default class HeroModule { }
export class HeroModule { }

View File

@ -1,10 +1,10 @@
import { ModuleWithProviders } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component.3';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroComponent } from './hero.component.3';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',

View File

@ -1,10 +1,10 @@
import { ModuleWithProviders } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroComponent } from './hero.component';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',

View File

@ -1,40 +1,18 @@
// #docregion
import { NgModule,
ModuleWithProviders } from '@angular/core';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
// #docregion shared-module
// #docregion module
@NgModule({
imports: [ CommonModule ],
declarations: [ AwesomePipe, HighlightDirective, TitleComponent ],
exports: [ AwesomePipe, HighlightDirective, TitleComponent,
declarations: [ AwesomePipe, HighlightDirective ],
exports: [ AwesomePipe, HighlightDirective,
CommonModule, FormsModule ]
})
export class SharedModule {
// #docregion for-root
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [ UserService ]
};
}
// #enddocregion for-root
}
// #enddocregion shared-module
export class SharedModule { }
// #enddocregion module
// #enddocregion
// #docregion shared-root-module
@NgModule({
exports: [ SharedModule ],
providers: [ UserService ]
})
export class SharedRootModule { }
// #enddocregion shared-root-module

View File

@ -1,20 +0,0 @@
// Crazy copy of the app/user.service
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable } from '@angular/core';
@Injectable()
export class UserService {
static userName = '';
constructor() {
UserService.userName += UserService.userName || 'Sam Spade';
}
get userName() { return UserService.userName; }
}

View File

@ -4,5 +4,5 @@ import { Injectable } from '@angular/core';
@Injectable()
/** Dummy version of an authenticated user service */
export class UserService {
userName = 'Sam Spade';
userName = 'Sherlock Holmes';
}

View File

@ -9,11 +9,12 @@
"app/title.component.ts",
"app/user.service.ts",
"app/contact/*.css",
"app/contact/*.html",
"app/contact/*.ts",
"!app/contact/contact.component.ts",
"!app/contact/contact.module.ts",
"app/contact/awesome.pipe.ts",
"app/contact/contact.component.css",
"app/contact/contact.component.html",
"app/contact/contact.component.3.ts",
"app/contact/contact.service.ts",
"app/contact/highlight.directive.ts",
"styles.css",
"index.1b.html"

View File

@ -9,12 +9,14 @@
"app/title.component.ts",
"app/user.service.ts",
"app/contact/*.css",
"app/contact/*.html",
"app/contact/*.ts",
"!app/contact/contact.component.ts",
"!app/contact/contact.module.ts",
"!app/contact/contact.module.3.ts",
"app/contact/contact.component.css",
"app/contact/contact.component.html",
"app/contact/contact.service.ts",
"app/contact/awesome.pipe.ts",
"app/contact/contact.component.3.ts",
"app/contact/contact.module.2.ts",
"app/contact/highlight.directive.ts",
"styles.css",
"index.2.html"

View File

@ -8,19 +8,25 @@
"app/contact/contact.component.css",
"app/contact/contact.component.html",
"app/contact/contact.service.ts",
"app/contact/contact.component.ts",
"app/contact/contact.module.ts",
"app/contact/contact.routing.ts",
"app/contact/contact.service.ts",
"app/crisis/*.ts",
"app/hero/*.ts",
"app/hero/hero-detail.component.ts",
"app/hero/hero-list.component.ts",
"app/hero/hero.service.ts",
"!app/hero/hero.component.3.ts",
"!app/hero/hero.module.3.ts",
"!app/hero/hero.routing.3.ts",
"!app/hero/highlight.directive.ts",
"app/hero/hero.component.ts",
"app/hero/hero.module.ts",
"app/hero/hero.routing.ts",
"app/core/*.css",
"app/core/*.html",
"app/core/*.ts",
"app/shared/*.css",
"app/shared/*.html",

View File

@ -11,21 +11,26 @@
"app/title.component.ts",
"app/user.service.ts",
"app/contact/*.css",
"app/contact/*.html",
"app/contact/*.ts",
"app/contact/contact.component.css",
"app/contact/contact.component.html",
"app/contact/contact.service.ts",
"!app/contact/contact.component.ts",
"!app/contact/contact.module.ts",
"!app/contact/contact.routing.ts",
"app/contact/awesome.pipe.ts",
"app/contact/contact.component.3.ts",
"app/contact/contact.module.3.ts",
"app/contact/contact.routing.3.ts",
"app/contact/highlight.directive.ts",
"app/crisis/*.ts",
"app/hero/*.ts",
"app/hero/hero-detail.component.ts",
"app/hero/hero-list.component.ts",
"app/hero/hero.service.ts",
"!app/hero/hero.component.ts",
"!app/hero/hero.module.ts",
"!app/hero/hero.routing.ts",
"app/hero/hero.component.3.ts",
"app/hero/hero.module.3.ts",
"app/hero/hero.routing.3.ts",
"app/hero/highlight.directive.ts",
"styles.css",
"index.3.html"

View File

@ -25,29 +25,29 @@
"author": "",
"license": "ISC",
"dependencies": {
"@angular/common": "2.0.0-rc.5",
"@angular/compiler": "2.0.0-rc.5",
"@angular/core": "2.0.0-rc.5",
"@angular/forms": "0.3.0",
"@angular/http": "2.0.0-rc.5",
"@angular/platform-browser": "2.0.0-rc.5",
"@angular/platform-browser-dynamic": "2.0.0-rc.5",
"@angular/router": "3.0.0-rc.1",
"@angular/router-deprecated": "2.0.0-rc.2",
"@angular/upgrade": "2.0.0-rc.5",
"angular2-in-memory-web-api": "0.0.17",
"@angular/common": "2.0.0-rc.6",
"@angular/compiler": "2.0.0-rc.6",
"@angular/compiler-cli": "0.6.0",
"@angular/core": "2.0.0-rc.6",
"@angular/forms": "2.0.0-rc.6",
"@angular/http": "2.0.0-rc.6",
"@angular/platform-browser": "2.0.0-rc.6",
"@angular/platform-browser-dynamic": "2.0.0-rc.6",
"@angular/router": "3.0.0-rc.2",
"@angular/upgrade": "2.0.0-rc.6",
"angular2-in-memory-web-api": "0.0.18",
"bootstrap": "^3.3.6",
"core-js": "^2.4.0",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"rxjs": "5.0.0-beta.11",
"systemjs": "0.19.27",
"zone.js": "^0.6.12"
"zone.js": "^0.6.17"
},
"devDependencies": {
"angular-cli": "^1.0.0-beta.5",
"angular2-template-loader": "^0.4.0",
"canonical-path": "0.0.2",
"concurrently": "^2.1.0",
"concurrently": "^2.2.0",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
@ -55,15 +55,15 @@
"html-webpack-plugin": "^2.16.1",
"http-server": "^0.9.0",
"jasmine-core": "^2.4.1",
"karma": "^0.13.22",
"karma-chrome-launcher": "^1.0.1",
"karma-cli": "^1.0.0",
"karma-htmlfile-reporter": "^0.3.1",
"karma": "^1.2.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-htmlfile-reporter": "^0.3.4",
"karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.7.0",
"lite-server": "^2.2.0",
"karma-webpack": "^1.8.0",
"lite-server": "^2.2.2",
"lodash": "^4.13.1",
"null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.7",
@ -73,9 +73,9 @@
"style-loader": "^0.13.1",
"ts-loader": "^0.8.2",
"ts-node": "^0.7.3",
"tslint": "^3.13.0",
"tslint": "^3.15.1",
"typescript": "^1.8.10",
"typings": "^1.0.4",
"typings": "^1.3.2",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1",
"webpack-merge": "^0.14.0"

Some files were not shown because too many files have changed in this diff Show More