977 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			977 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <html lang="en"><head></head><body>
 | |
|         <form id="mainForm" method="post" action="https://run.stackblitz.com/api/angular/v1?file=src/app/app.component.ts" target="_self"><input type="hidden" name="files[src/app/app.component.ts]" value="import { Component, OnInit } from '@angular/core';
 | |
| import { Item } from './item';
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-root',
 | |
|   templateUrl: './app.component.html',
 | |
|   styleUrls: ['./app.component.css']
 | |
| })
 | |
| export class AppComponent implements OnInit {
 | |
| 
 | |
|   canSave =  true;
 | |
|   isSpecial = true;
 | |
|   isUnchanged = true;
 | |
| 
 | |
|   isActive = true;
 | |
|   nullCustomer = null;
 | |
|   currentCustomer = {
 | |
|     name: 'Laura'
 | |
|   };
 | |
| 
 | |
|   item: Item; // defined to demonstrate template context precedence
 | |
|   items: Item[];
 | |
| 
 | |
|   currentItem: Item;
 | |
| 
 | |
| 
 | |
| 
 | |
|   // trackBy change counting
 | |
|   itemsNoTrackByCount   = 0;
 | |
|   itemsWithTrackByCount = 0;
 | |
|   itemsWithTrackByCountReset = 0;
 | |
|   itemIdIncrement = 1;
 | |
| 
 | |
|   currentClasses: {};
 | |
| 
 | |
|   currentStyles: {};
 | |
| 
 | |
|   ngOnInit() {
 | |
|     this.resetItems();
 | |
|     this.setCurrentClasses();
 | |
|     this.setCurrentStyles();
 | |
|     this.itemsNoTrackByCount = 0;
 | |
|   }
 | |
| 
 | |
|   setUppercaseName(name: string) {
 | |
|     this.currentItem.name = name.toUpperCase();
 | |
|   }
 | |
| 
 | |
|   setCurrentClasses() {
 | |
|     // CSS classes: added/removed per current state of component properties
 | |
|     this.currentClasses =  {
 | |
|       saveable: this.canSave,
 | |
|       modified: !this.isUnchanged,
 | |
|       special:  this.isSpecial
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   setCurrentStyles() {
 | |
|     // CSS styles: set per current state of component properties
 | |
|     this.currentStyles = {
 | |
|       'font-style':  this.canSave      ? 'italic' : 'normal',
 | |
|       'font-weight': !this.isUnchanged ? 'bold'   : 'normal',
 | |
|       'font-size':   this.isSpecial    ? '24px'   : '12px'
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   isActiveToggle() {
 | |
|     this.isActive = !this.isActive;
 | |
|   }
 | |
| 
 | |
|   giveNullCustomerValue() {
 | |
|     this.nullCustomer = 'Kelly';
 | |
|   }
 | |
| 
 | |
|   resetItems() {
 | |
|     this.items = Item.items.map(item => item.clone());
 | |
|     this.currentItem = this.items[0];
 | |
|     this.item = this.currentItem;
 | |
|   }
 | |
| 
 | |
|   resetList() {
 | |
|     this.resetItems();
 | |
|     this.itemsWithTrackByCountReset = 0;
 | |
|     this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
 | |
|   }
 | |
| 
 | |
|   changeIds() {
 | |
| 
 | |
|     this.items.forEach(i => i.id += 1 * this.itemIdIncrement);
 | |
|     this.itemsWithTrackByCountReset = -1;
 | |
|     this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
 | |
|     this.itemsWithTrackByCount = ++this.itemsWithTrackByCount;
 | |
|   }
 | |
| 
 | |
|   clearTrackByCounts() {
 | |
|     this.resetItems();
 | |
|     this.itemsNoTrackByCount = 0;
 | |
|     this.itemsWithTrackByCount = 0;
 | |
|     this.itemIdIncrement = 1;
 | |
|   }
 | |
|   trackByItems(index: number, item: Item): number { return item.id; }
 | |
| 
 | |
|   trackById(index: number, item: any): number { return item.id; }
 | |
| 
 | |
|   getValue(target: EventTarget): string {
 | |
|     return (target as HTMLInputElement).value;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/app.module.ts]" value="import { BrowserModule } from '@angular/platform-browser';
 | |
| import { NgModule } from '@angular/core';
 | |
| import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
 | |
| 
 | |
| 
 | |
| import { AppComponent } from './app.component';
 | |
| import { ItemDetailComponent } from './item-detail/item-detail.component';
 | |
| import { ItemSwitchComponents } from './item-switch.component';
 | |
| 
 | |
| 
 | |
| @NgModule({
 | |
|   declarations: [
 | |
|     AppComponent,
 | |
|     ItemDetailComponent,
 | |
|     ItemSwitchComponents
 | |
|   ],
 | |
| 
 | |
|   imports: [
 | |
|     BrowserModule,
 | |
|     FormsModule // <--- import into the NgModule
 | |
|   ],
 | |
|   providers: [],
 | |
|   bootstrap: [AppComponent]
 | |
| })
 | |
| export class AppModule { }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/item-detail/item-detail.component.ts]" value="import { Component, Input } from '@angular/core';
 | |
| 
 | |
| import { Item } from '../item';
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-item-detail',
 | |
|   templateUrl: './item-detail.component.html',
 | |
|   styleUrls: ['./item-detail.component.css']
 | |
| })
 | |
| export class ItemDetailComponent {
 | |
| 
 | |
| 
 | |
|   @Input() item: Item;
 | |
| 
 | |
|   constructor() { }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/item-switch.component.ts]" value="import { Component, Input } from '@angular/core';
 | |
| import { Item } from './item';
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-stout-item',
 | |
|   template: `I'm a little {{item.name}}, short and stout!`
 | |
| })
 | |
| 
 | |
| export class StoutItemComponent {
 | |
|   @Input() item: Item;
 | |
| }
 | |
| 
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-best-item',
 | |
|   template: `This is the brightest {{item.name}} in town.`
 | |
| })
 | |
| export class BestItemComponent {
 | |
|   @Input() item: Item;
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-device-item',
 | |
|   template: `Which is the slimmest {{item.name}}?`
 | |
| })
 | |
| export class DeviceItemComponent {
 | |
|   @Input() item: Item;
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-lost-item',
 | |
|   template: `Has anyone seen my {{item.name}}?`
 | |
| })
 | |
| export class LostItemComponent {
 | |
|   @Input() item: Item;
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-unknown-item',
 | |
|   template: `{{message}}`
 | |
| })
 | |
| export class UnknownItemComponent {
 | |
|   @Input() item: Item;
 | |
|   get message() {
 | |
|     return this.item && this.item.name ?
 | |
|       `${this.item.name} is strange and mysterious.` :
 | |
|       'A mystery wrapped in a fishbowl.';
 | |
|   }
 | |
| }
 | |
| 
 | |
| export const ItemSwitchComponents =
 | |
|   [ StoutItemComponent, BestItemComponent, DeviceItemComponent, LostItemComponent, UnknownItemComponent ];
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/item.ts]" value="export class Item {
 | |
|   static nextId = 0;
 | |
| 
 | |
|   static items: Item[] = [
 | |
|     new Item(
 | |
|       null,
 | |
|       'Teapot',
 | |
|       'stout'
 | |
|     ),
 | |
|     new Item(1, 'Lamp', 'bright'),
 | |
|     new Item(2, 'Phone', 'slim' ),
 | |
|     new Item(3, 'Television', 'vintage' ),
 | |
|     new Item(4, 'Fishbowl')
 | |
|   ];
 | |
| 
 | |
| 
 | |
|   constructor(
 | |
|     public id?: number,
 | |
|     public name?: string,
 | |
|     public feature?: string,
 | |
|     public url?: string,
 | |
|     public rate = 100,
 | |
|     ) {
 | |
|     this.id = id ? id : Item.nextId++;
 | |
|   }
 | |
| 
 | |
|   clone(): Item {
 | |
|     return Object.assign(new Item(), this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/environments/environment.prod.ts]" value="export const environment = {
 | |
|   production: true
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/environments/environment.ts]" value="// This file can be replaced during build by using the `fileReplacements` array.
 | |
| // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
 | |
| // The list of file replacements can be found in `angular.json`.
 | |
| 
 | |
| export const environment = {
 | |
|   production: false
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * For easier debugging in development mode, you can import the following file
 | |
|  * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
 | |
|  *
 | |
|  * This import should be commented out in production mode because it will have a negative impact
 | |
|  * on performance if an error is thrown.
 | |
|  */
 | |
| // import 'zone.js/plugins/zone-error';  // Included with Angular CLI.
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/main.ts]" value="import { enableProdMode } from '@angular/core';
 | |
| import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 | |
| 
 | |
| import { AppModule } from './app/app.module';
 | |
| import { environment } from './environments/environment';
 | |
| 
 | |
| if (environment.production) {
 | |
|   enableProdMode();
 | |
| }
 | |
| 
 | |
| platformBrowserDynamic().bootstrapModule(AppModule)
 | |
|   .catch(err => console.log(err));
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/polyfills.ts]" value="/**
 | |
|  * This file includes polyfills needed by Angular and is loaded before the app.
 | |
|  * You can add your own extra polyfills to this file.
 | |
|  *
 | |
|  * This file is divided into 2 sections:
 | |
|  *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 | |
|  *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 | |
|  *      file.
 | |
|  *
 | |
|  * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 | |
|  * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 | |
|  * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 | |
|  *
 | |
|  * Learn more in https://angular.io/guide/browser-support
 | |
|  */
 | |
| 
 | |
| /***************************************************************************************************
 | |
|  * BROWSER POLYFILLS
 | |
|  */
 | |
| 
 | |
| /** IE11 requires the following for NgClass support on SVG elements */
 | |
| // import 'classlist.js';  // Run `npm install --save classlist.js`.
 | |
| 
 | |
| /**
 | |
|  * Web Animations `@angular/platform-browser/animations`
 | |
|  * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
 | |
|  * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
 | |
|  */
 | |
| // import 'web-animations-js';  // Run `npm install --save web-animations-js`.
 | |
| 
 | |
| /**
 | |
|  * By default, zone.js will patch all possible macroTask and DomEvents
 | |
|  * user can disable parts of macroTask/DomEvents patch by setting following flags
 | |
|  * because those flags need to be set before `zone.js` being loaded, and webpack
 | |
|  * will put import in the top of bundle, so user need to create a separate file
 | |
|  * in this directory (for example: zone-flags.ts), and put the following flags
 | |
|  * into that file, and then add the following code before importing zone.js.
 | |
|  * import './zone-flags';
 | |
|  *
 | |
|  * The flags allowed in zone-flags.ts are listed here.
 | |
|  *
 | |
|  * The following flags will work for all browsers.
 | |
|  *
 | |
|  * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch
 | |
|  * requestAnimationFrame
 | |
|  * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 | |
|  * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch
 | |
|  * specified eventNames
 | |
|  *
 | |
|  *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 | |
|  *  with the following flag, it will bypass `zone.js` patch for IE/Edge
 | |
|  *
 | |
|  *  (window as any).__Zone_enable_cross_context_check = true;
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /***************************************************************************************************
 | |
|  * Zone JS is required by default for Angular itself.
 | |
|  */
 | |
| import 'zone.js'; // Included with Angular CLI.
 | |
| 
 | |
| /***************************************************************************************************
 | |
|  * APPLICATION IMPORTS
 | |
|  */
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/app.component.css]" value="
 | |
| button {
 | |
| 	font-size: 100%;
 | |
| 	margin: 0 2px;
 | |
| }
 | |
| 
 | |
| div[ng-reflect-ng-switch], app-unknown-item {
 | |
|   margin: .5rem 0;
 | |
|   display: block;
 | |
| }
 | |
| 
 | |
| #noTrackByCnt,
 | |
| #withTrackByCnt {
 | |
| 	color: darkred;
 | |
| 	max-width: 450px;
 | |
| 	margin: 4px;
 | |
| }
 | |
| 
 | |
| img {
 | |
| 	height: 100px;
 | |
| }
 | |
| 
 | |
| .box {
 | |
| 	border: 1px solid black;
 | |
| 	padding: 6px;
 | |
| 	max-width: 450px;
 | |
| }
 | |
| 
 | |
| .child-div {
 | |
| 	margin-left: 1em;
 | |
| 	font-weight: normal;
 | |
| }
 | |
| 
 | |
| .context {
 | |
| 	margin-left: 1em;
 | |
| }
 | |
| 
 | |
| .hidden {
 | |
| 	display: none;
 | |
| }
 | |
| 
 | |
| .parent-div {
 | |
| 	margin-top: 1em;
 | |
| 	font-weight: bold;
 | |
| }
 | |
| 
 | |
| .course {
 | |
| 	font-weight: bold;
 | |
| 	font-size: x-large;
 | |
| }
 | |
| 
 | |
| .helpful {
 | |
| 	color: red;
 | |
| }
 | |
| 
 | |
| .saveable {
 | |
| 	color: limegreen;
 | |
| }
 | |
| 
 | |
| .study,
 | |
| .modified {
 | |
|   font-family: "Brush Script MT", cursive;
 | |
|   font-size: 2rem;
 | |
| }
 | |
| 
 | |
| .toe {
 | |
| 	margin-left: 1em;
 | |
| 	font-style: italic;
 | |
| }
 | |
| 
 | |
| 
 | |
| .to-toc {
 | |
| 	margin-top: 10px;
 | |
| 	display: block;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/item-detail/item-detail.component.css]" value="
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/styles.css]" value="/* Global Styles */
 | |
| * {
 | |
|   font-family: Arial, Helvetica, sans-serif;
 | |
| }
 | |
| h1 {
 | |
|   color: #264D73;
 | |
|   font-size: 2.5rem;
 | |
| }
 | |
| h2, h3 {
 | |
|   color: #444;
 | |
|   font-weight: lighter;
 | |
| }
 | |
| h3 {
 | |
|   font-size: 1.3rem;
 | |
| }
 | |
| body {
 | |
|   padding: .5rem;
 | |
|   max-width: 1000px;
 | |
|   margin: auto;
 | |
| }
 | |
| @media (min-width: 600px) {
 | |
|   body {
 | |
|     padding: 2rem;
 | |
|   }
 | |
| }
 | |
| body, input[text] {
 | |
|   color: #333;
 | |
|   font-family: Cambria, Georgia, serif;
 | |
| }
 | |
| a {
 | |
|   cursor: pointer;
 | |
| }
 | |
| button {
 | |
|   background-color: #eee;
 | |
|   border: none;
 | |
|   border-radius: 4px;
 | |
|   cursor: pointer;
 | |
|   color: black;
 | |
|   font-size: 1.2rem;
 | |
|   padding: 1rem;
 | |
|   margin-right: 1rem;
 | |
|   margin-bottom: 1rem;
 | |
| }
 | |
| button:hover {
 | |
|   background-color: black;
 | |
|   color: white;
 | |
| }
 | |
| button:disabled {
 | |
|   background-color: #eee;
 | |
|   color: #aaa;
 | |
|   cursor: auto;
 | |
| }
 | |
| 
 | |
| /* Navigation link styles */
 | |
| nav a {
 | |
|   padding: 5px 10px;
 | |
|   text-decoration: none;
 | |
|   margin-right: 10px;
 | |
|   margin-top: 10px;
 | |
|   display: inline-block;
 | |
|   background-color: #e8e8e8;
 | |
|   color: #3d3d3d;
 | |
|   border-radius: 4px;
 | |
| }
 | |
| 
 | |
| nav a:hover {
 | |
|   color: white;
 | |
|   background-color:  #42545C;
 | |
| }
 | |
| nav a.active {
 | |
|   background-color: black;
 | |
|   color: white;
 | |
| }
 | |
| hr {
 | |
|   margin: 1.5rem 0;
 | |
| }
 | |
| input[type="text"] {
 | |
|   box-sizing: border-box;
 | |
|   width: 100%;
 | |
|   padding: .5rem;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| */"><input type="hidden" name="files[src/app/app.component.html]" value="<h1>Built-in Directives</h1>
 | |
| 
 | |
| <h2>Built-in attribute directives</h2>
 | |
| 
 | |
| <h3 id="ngModel">NgModel (two-way) Binding</h3>
 | |
| 
 | |
| <fieldset><h4>NgModel examples</h4>
 | |
|   <p>Current item name: {{currentItem.name}}</p>
 | |
|   <p>
 | |
|     <label for="without">without NgModel:</label>
 | |
|     <input [value]="currentItem.name" (input)="currentItem.name=getValue($event.target)" id="without">
 | |
|   </p>
 | |
| 
 | |
|   <p>
 | |
|     <label for="example-ngModel">[(ngModel)]:</label>
 | |
|     <input [(ngModel)]="currentItem.name" id="example-ngModel">
 | |
|   </p>
 | |
| 
 | |
|   <p>
 | |
|     <label for="example-bindon">bindon-ngModel: </label>
 | |
|     <input bindon-ngModel="currentItem.name" id="example-bindon">
 | |
|   </p>
 | |
| 
 | |
|   <p>
 | |
|     <label for="example-change">(ngModelChange)="...name=$event":</label>
 | |
|     <input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">
 | |
|   </p>
 | |
| 
 | |
|   <p>
 | |
|     <label for="example-uppercase">(ngModelChange)="setUppercaseName($event)"
 | |
|       <input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">
 | |
|     </label>
 | |
|   </p>
 | |
| </fieldset>
 | |
| 
 | |
| <hr><h2 id="ngClass">NgClass Binding</h2>
 | |
| 
 | |
| <p>currentClasses is {{currentClasses | json}}</p>
 | |
| <div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>
 | |
| <ul>
 | |
|   <li>
 | |
|     <label for="saveable">saveable</label>
 | |
|     <input type="checkbox" [(ngModel)]="canSave" id="saveable">
 | |
|   </li>
 | |
|   <li>
 | |
|     <label for="modified">modified:</label>
 | |
|     <input type="checkbox" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged" id="modified"></li>
 | |
|   <li>
 | |
|     <label for="special">special: <input type="checkbox" [(ngModel)]="isSpecial" id="special"></label>
 | |
| </li>
 | |
| </ul>
 | |
| <button (click)="setCurrentClasses()">Refresh currentClasses</button>
 | |
| 
 | |
| <div [ngClass]="currentClasses">
 | |
|   This div should be {{ canSave ? "": "not"}} saveable,
 | |
|                   {{ isUnchanged ? "unchanged" : "modified" }} and
 | |
|                   {{ isSpecial ? "": "not"}} special after clicking "Refresh".</div>
 | |
| <br><br>
 | |
| <!-- toggle the "special" class on/off with a property -->
 | |
| <div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
 | |
| <div class="helpful study course">Helpful study course</div>
 | |
| <div [ngClass]="{'helpful':false, 'study':true, 'course':true}">Study course</div>
 | |
| 
 | |
| 
 | |
| <!-- NgStyle binding -->
 | |
| <hr><h3>NgStyle Binding</h3>
 | |
| <div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
 | |
|   This div is x-large or smaller.
 | |
| </div>
 | |
| 
 | |
| <h4>[ngStyle] binding to currentStyles - CSS property names</h4>
 | |
| <p>currentStyles is {{currentStyles | json}}</p>
 | |
| 
 | |
| <div [ngStyle]="currentStyles">
 | |
|   This div is initially italic, normal weight, and extra large (24px).
 | |
| </div>
 | |
| 
 | |
| 
 | |
| 
 | |
| <br>
 | |
| <label>italic: <input type="checkbox" [(ngModel)]="canSave"></label> |
 | |
| <label>normal: <input type="checkbox" [(ngModel)]="isUnchanged"></label> |
 | |
| <label>xlarge: <input type="checkbox" [(ngModel)]="isSpecial"></label>
 | |
| <button (click)="setCurrentStyles()">Refresh currentStyles</button>
 | |
| <br><br>
 | |
| <div [ngStyle]="currentStyles">
 | |
|   This div should be {{ canSave ? "italic": "plain"}},
 | |
|                   {{ isUnchanged ? "normal weight" : "bold" }} and,
 | |
|                   {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div>
 | |
| 
 | |
| <hr>
 | |
| <h2>Built-in structural directives</h2>
 | |
| <h3 id="ngIf">NgIf Binding</h3>
 | |
| <div>
 | |
|   <p>If isActive is true, app-item-detail will render: </p>
 | |
|   <app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
 | |
| 
 | |
|   <button (click)="isActiveToggle()">Toggle app-item-detail</button>
 | |
| </div>
 | |
| <p>If currentCustomer isn't null, say hello to Laura:</p>
 | |
| <div *ngIf="currentCustomer">Hello, {{currentCustomer.name}}</div>
 | |
| <p>nullCustomer is null by default. NgIf guards against null. Give it a value to show it:</p>
 | |
| <div *ngIf="nullCustomer">Hello, <span>{{nullCustomer}}</span></div>
 | |
| <button (click)="giveNullCustomerValue()">Give nullCustomer a value</button>
 | |
| 
 | |
| 
 | |
| <h4>NgIf binding with template (no *)</h4>
 | |
| 
 | |
| <ng-template [ngIf]="currentItem">Add {{currentItem.name}} with template</ng-template>
 | |
| <hr>
 | |
| 
 | |
| <h4>Show/hide vs. NgIf</h4>
 | |
| <!-- isSpecial is true -->
 | |
| <div [class.hidden]="!isSpecial">Show with class</div>
 | |
| <div [class.hidden]="isSpecial">Hide with class</div>
 | |
| 
 | |
| <p>ItemDetail is in the DOM but hidden</p>
 | |
| <app-item-detail [class.hidden]="isSpecial"></app-item-detail>
 | |
| 
 | |
| <div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
 | |
| <div [style.display]="isSpecial ? 'none'  : 'block'">Hide with style</div>
 | |
| 
 | |
| 
 | |
| <hr>
 | |
| <h2 id="ngFor">NgFor Binding</h2>
 | |
| 
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items">{{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <p>*ngFor with ItemDetailComponent element</p>
 | |
| <div class="box">
 | |
|   <app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>
 | |
| </div>
 | |
| 
 | |
| 
 | |
| <h4 id="ngFor-index">*ngFor with index</h4>
 | |
| <p>with <i>semi-colon</i> separator</p>
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items; let i=index">{{i + 1}} - {{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <p>with <i>comma</i> separator</p>
 | |
| <div class="box">
 | |
|  <div *ngFor="let item of items, let i=index">{{i + 1}} - {{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <h4 id="ngFor-trackBy">*ngFor trackBy</h4>
 | |
| <button (click)="resetList()">Reset items</button>
 | |
| <button (click)="changeIds()">Change ids</button>
 | |
| <button (click)="clearTrackByCounts()">Clear counts</button>
 | |
| 
 | |
| <p><i>without</i> trackBy</p>
 | |
| <div class="box">
 | |
|   <div #noTrackBy *ngFor="let item of items">({{item.id}}) {{item.name}}</div>
 | |
| 
 | |
|   <div id="noTrackByCnt" *ngIf="itemsNoTrackByCount" >
 | |
|     Item DOM elements change #{{itemsNoTrackByCount}} without trackBy
 | |
|   </div>
 | |
| </div>
 | |
| 
 | |
| <p>with trackBy</p>
 | |
| <div class="box">
 | |
|   <div #withTrackBy *ngFor="let item of items; trackBy: trackByItems">({{item.id}}) {{item.name}}</div>
 | |
| 
 | |
|   <div id="withTrackByCnt" *ngIf="itemsWithTrackByCount">
 | |
|     Item DOM elements change #{{itemsWithTrackByCount}} with trackBy
 | |
|   </div>
 | |
| </div>
 | |
| 
 | |
| <br><br><br>
 | |
| 
 | |
| <p>with trackBy and <i>semi-colon</i> separator</p>
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items; trackBy: trackByItems">
 | |
|     ({{item.id}}) {{item.name}}
 | |
|   </div>
 | |
| </div>
 | |
| 
 | |
| <p>with trackBy and <i>comma</i> separator</p>
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items, trackBy: trackByItems">({{item.id}}) {{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <p>with trackBy and <i>space</i> separator</p>
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items trackBy: trackByItems">({{item.id}}) {{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <p>with <i>generic</i> trackById function</p>
 | |
| <div class="box">
 | |
|   <div *ngFor="let item of items, trackBy: trackById">({{item.id}}) {{item.name}}</div>
 | |
| </div>
 | |
| 
 | |
| <hr><h2>NgSwitch Binding</h2>
 | |
| 
 | |
| <p>Pick your favorite item</p>
 | |
| <div>
 | |
|   <label *ngFor="let i of items">
 | |
|     <div><input type="radio" name="items" [(ngModel)]="currentItem" [value]="i">{{i.name}}
 | |
|     </div>
 | |
|   </label>
 | |
| </div>
 | |
| 
 | |
| <div [ngSwitch]="currentItem.feature">
 | |
|   <app-stout-item    *ngSwitchCase="'stout'"    [item]="currentItem"></app-stout-item>
 | |
|   <app-device-item   *ngSwitchCase="'slim'"     [item]="currentItem"></app-device-item>
 | |
|   <app-lost-item     *ngSwitchCase="'vintage'"  [item]="currentItem"></app-lost-item>
 | |
|   <app-best-item     *ngSwitchCase="'bright'"   [item]="currentItem"></app-best-item>
 | |
|   <div *ngSwitchCase="'bright'"> Are you as bright as {{currentItem.name}}?</div>
 | |
|   <app-unknown-item  *ngSwitchDefault           [item]="currentItem"></app-unknown-item>
 | |
| </div>
 | |
| 
 | |
| 
 | |
| 
 | |
| <!-- 
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| -->"><input type="hidden" name="files[src/app/item-detail/item-detail.component.html]" value="<div>
 | |
|   <span>{{item?.name}}</span>
 | |
| </div>
 | |
| 
 | |
| 
 | |
| <!-- 
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| -->"><input type="hidden" name="files[src/index.html]" value="<!doctype html>
 | |
| <html lang="en">
 | |
| <head>
 | |
|   <meta charset="utf-8">
 | |
|   <title>BuiltInDirectives</title>
 | |
|   <base href="/">
 | |
|   <meta name="viewport" content="width=device-width, initial-scale=1">
 | |
|   <link rel="icon" type="image/x-icon" href="favicon.ico">
 | |
| </head>
 | |
| <body>
 | |
|   <app-root></app-root>
 | |
| </body>
 | |
| </html>
 | |
| 
 | |
| 
 | |
| <!-- 
 | |
| Copyright Google LLC. All Rights Reserved.
 | |
| Use of this source code is governed by an MIT-style license that
 | |
| can be found in the LICENSE file at https://angular.io/license
 | |
| -->"><input type="hidden" name="files[angular.json]" value="{
 | |
|   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
 | |
|   "version": 1,
 | |
|   "newProjectRoot": "projects",
 | |
|   "projects": {
 | |
|     "angular.io-example": {
 | |
|       "projectType": "application",
 | |
|       "schematics": {
 | |
|         "@schematics/angular:application": {
 | |
|           "strict": true
 | |
|         }
 | |
|       },
 | |
|       "root": "",
 | |
|       "sourceRoot": "src",
 | |
|       "prefix": "app",
 | |
|       "architect": {
 | |
|         "build": {
 | |
|           "builder": "@angular-devkit/build-angular:browser",
 | |
|           "options": {
 | |
|             "outputPath": "dist",
 | |
|             "index": "src/index.html",
 | |
|             "main": "src/main.ts",
 | |
|             "polyfills": "src/polyfills.ts",
 | |
|             "tsConfig": "tsconfig.app.json",
 | |
|             "aot": true,
 | |
|             "assets": [
 | |
|               "src/favicon.ico",
 | |
|               "src/assets"
 | |
|             ],
 | |
|             "styles": [
 | |
|               "src/styles.css"
 | |
|             ],
 | |
|             "scripts": []
 | |
|           },
 | |
|           "configurations": {
 | |
|             "production": {
 | |
|               "fileReplacements": [
 | |
|                 {
 | |
|                   "replace": "src/environments/environment.ts",
 | |
|                   "with": "src/environments/environment.prod.ts"
 | |
|                 }
 | |
|               ],
 | |
|               "optimization": true,
 | |
|               "outputHashing": "all",
 | |
|               "sourceMap": false,
 | |
|               "namedChunks": false,
 | |
|               "extractLicenses": true,
 | |
|               "vendorChunk": false,
 | |
|               "buildOptimizer": true,
 | |
|               "budgets": [
 | |
|                 {
 | |
|                   "type": "initial",
 | |
|                   "maximumWarning": "500kb",
 | |
|                   "maximumError": "1mb"
 | |
|                 },
 | |
|                 {
 | |
|                   "type": "anyComponentStyle",
 | |
|                   "maximumWarning": "2kb",
 | |
|                   "maximumError": "4kb"
 | |
|                 }
 | |
|               ]
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         "serve": {
 | |
|           "builder": "@angular-devkit/build-angular:dev-server",
 | |
|           "options": {
 | |
|             "browserTarget": "angular.io-example:build"
 | |
|           },
 | |
|           "configurations": {
 | |
|             "production": {
 | |
|               "browserTarget": "angular.io-example:build:production"
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         "extract-i18n": {
 | |
|           "builder": "@angular-devkit/build-angular:extract-i18n",
 | |
|           "options": {
 | |
|             "browserTarget": "angular.io-example:build"
 | |
|           }
 | |
|         },
 | |
|         "test": {
 | |
|           "builder": "@angular-devkit/build-angular:karma",
 | |
|           "options": {
 | |
|             "main": "src/test.ts",
 | |
|             "polyfills": "src/polyfills.ts",
 | |
|             "tsConfig": "tsconfig.spec.json",
 | |
|             "karmaConfig": "karma.conf.js",
 | |
|             "assets": [
 | |
|               "src/favicon.ico",
 | |
|               "src/assets"
 | |
|             ],
 | |
|             "styles": [
 | |
|               "src/styles.css"
 | |
|             ],
 | |
|             "scripts": []
 | |
|           }
 | |
|         },
 | |
|         "lint": {
 | |
|           "builder": "@angular-devkit/build-angular:tslint",
 | |
|           "options": {
 | |
|             "tsConfig": [
 | |
|               "tsconfig.app.json",
 | |
|               "tsconfig.spec.json",
 | |
|               "e2e/tsconfig.json"
 | |
|             ],
 | |
|             "exclude": [
 | |
|               "**/node_modules/**"
 | |
|             ]
 | |
|           }
 | |
|         },
 | |
|         "e2e": {
 | |
|           "builder": "@angular-devkit/build-angular:protractor",
 | |
|           "options": {
 | |
|             "protractorConfig": "e2e/protractor.conf.js",
 | |
|             "devServerTarget": "angular.io-example:serve"
 | |
|           },
 | |
|           "configurations": {
 | |
|             "production": {
 | |
|               "devServerTarget": "angular.io-example:serve:production"
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   "defaultProject": "angular.io-example"
 | |
| }
 | |
| "><input type="hidden" name="files[tsconfig.json]" value="{
 | |
|   "compileOnSave": false,
 | |
|   "compilerOptions": {
 | |
|     "baseUrl": "./",
 | |
|     "outDir": "./dist/out-tsc",
 | |
|     "forceConsistentCasingInFileNames": true,
 | |
|     "noImplicitReturns": true,
 | |
|     "noFallthroughCasesInSwitch": true,
 | |
|     "sourceMap": true,
 | |
|     "declaration": false,
 | |
|     "downlevelIteration": true,
 | |
|     "experimentalDecorators": true,
 | |
|     "moduleResolution": "node",
 | |
|     "importHelpers": true,
 | |
|     "target": "es2015",
 | |
|     "module": "es2020",
 | |
|     "lib": [
 | |
|       "es2018",
 | |
|       "dom"
 | |
|     ]
 | |
|   },
 | |
|   "angularCompilerOptions": {
 | |
|     "strictInjectionParameters": true,
 | |
|     "strictInputAccessModifiers": true,
 | |
|     "strictTemplates": true,
 | |
|     "enableIvy": true
 | |
|   }
 | |
| }"><input type="hidden" name="tags[0]" value="angular"><input type="hidden" name="tags[1]" value="example"><input type="hidden" name="tags[2]" value="Built-in Directives"><input type="hidden" name="description" value="Angular Example - Built-in Directives"><input type="hidden" name="dependencies" value="{"@angular/animations":"~11.0.1","@angular/common":"~11.0.1","@angular/compiler":"~11.0.1","@angular/core":"~11.0.1","@angular/forms":"~11.0.1","@angular/platform-browser":"~11.0.1","@angular/platform-browser-dynamic":"~11.0.1","@angular/router":"~11.0.1","angular-in-memory-web-api":"~0.11.0","rxjs":"~6.6.0","tslib":"^2.0.0","zone.js":"~0.11.4","jasmine-core":"~3.6.0","jasmine-marbles":"~0.6.0"}"></form>
 | |
|         <script>
 | |
|           var embedded = 'ctl=1';
 | |
|           var isEmbedded = window.location.search.indexOf(embedded) > -1;
 | |
| 
 | |
|           if (isEmbedded) {
 | |
|             var form = document.getElementById('mainForm');
 | |
|             var action = form.action;
 | |
|             var actionHasParams = action.indexOf('?') > -1;
 | |
|             var symbol = actionHasParams ? '&' : '?'
 | |
|             form.action = form.action + symbol + embedded;
 | |
|           }
 | |
|           document.getElementById("mainForm").submit();
 | |
|         </script>
 | |
|       </body></html> |