diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 730f816c81..4de038f3d0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -533,6 +533,8 @@ /aio/content/examples/interpolation/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/images/guide/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes +/aio/content/examples/binding-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes + /aio/content/guide/pipes.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes /aio/content/examples/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes diff --git a/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts b/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..c9bc11e472 --- /dev/null +++ b/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts @@ -0,0 +1,76 @@ +import { browser, element, by } from 'protractor'; +import { logging } from 'selenium-webdriver'; + +describe('Binding syntax e2e tests', () => { + + beforeEach(function () { + browser.get(''); + }); + + + // helper function used to test what's logged to the console + async function logChecker(button, contents) { + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false); + expect(message.length).toBeGreaterThan(0); + } + + + it('should display Binding syntax', function () { + expect(element(by.css('h1')).getText()).toEqual('Binding syntax'); + }); + + it('should display Save button', function () { + expect(element.all(by.css('button')).get(0).getText()).toBe('Save'); + }); + + it('should display HTML attributes and DOM properties', function () { + expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties'); + }); + + it('should display 1. Use the inspector...', function () { + expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector'); + }); + + it('should display Disabled property vs. attribute', function () { + expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute'); + }); + + + it('should log a message including Sarah', async () => { + let attributeButton = element.all(by.css('button')).get(1); + await attributeButton.click(); + const contents = 'Sarah'; + logChecker(attributeButton, contents); + }); + + it('should log a message including Sarah for DOM property', async () => { + let DOMPropertyButton = element.all(by.css('button')).get(2); + await DOMPropertyButton.click(); + const contents = 'Sarah'; + logChecker(DOMPropertyButton, contents); + }); + + it('should log a message including Sally for DOM property', async () => { + let DOMPropertyButton = element.all(by.css('button')).get(2); + let input = element(by.css('input')); + input.sendKeys('Sally'); + await DOMPropertyButton.click(); + const contents = 'Sally'; + logChecker(DOMPropertyButton, contents); + }); + + it('should log a message that Test Button works', async () => { + let testButton = element.all(by.css('button')).get(3); + await testButton.click(); + const contents = 'Test'; + logChecker(testButton, contents); + }); + + it('should toggle Test Button disabled', async () => { + let toggleButton = element.all(by.css('button')).get(4); + await toggleButton.click(); + const contents = 'true'; + logChecker(toggleButton, contents); + }); +}); diff --git a/aio/content/examples/binding-syntax/example-config.json b/aio/content/examples/binding-syntax/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/binding-syntax/src/app/app.component.css b/aio/content/examples/binding-syntax/src/app/app.component.css new file mode 100644 index 0000000000..30428daac5 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/app/app.component.css @@ -0,0 +1,3 @@ +div { + padding: .25rem 0; +} diff --git a/aio/content/examples/binding-syntax/src/app/app.component.html b/aio/content/examples/binding-syntax/src/app/app.component.html new file mode 100644 index 0000000000..0260bd75a5 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/app/app.component.html @@ -0,0 +1,45 @@ + +
+

Binding syntax

+
+ +
+

Button disabled state bound to isUnchanged property

+ + + + +
+ +
+ +
+

HTML attributes and DOM properties

+

1. Use the inspector to see the HTML attribute and DOM property values. Click the buttons to log values to the console.

+ + +
+ Won't change. +
+ +
+ Changeable. Angular works with these. +
+ +

2. Change the name in the input and click the buttons again.

+
+ +
+ +
+

Disabled property vs. attribute

+

Use the inspector to see the Test Button work and its disabled property toggle.

+
+ +
+
+ +
+ +
diff --git a/aio/content/examples/binding-syntax/src/app/app.component.spec.ts b/aio/content/examples/binding-syntax/src/app/app.component.spec.ts new file mode 100644 index 0000000000..bcbdf36b3e --- /dev/null +++ b/aio/content/examples/binding-syntax/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + it('should create the app', async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + })); + it(`should have as title 'app'`, async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('app'); + })); + it('should render title in a h1 tag', async(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); + })); +}); diff --git a/aio/content/examples/binding-syntax/src/app/app.component.ts b/aio/content/examples/binding-syntax/src/app/app.component.ts new file mode 100644 index 0000000000..3349acbd47 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/app/app.component.ts @@ -0,0 +1,33 @@ +import { Component, ViewChild, ElementRef } from '@angular/core'; + + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + + @ViewChild('bindingInput', { static: false }) bindingInput: ElementRef; + + isUnchanged = true; + + getHTMLAttributeValue(): any { + console.warn('HTML attribute value: ' + this.bindingInput.nativeElement.getAttribute('value')); + } + + getDOMPropertyValue(): any { + console.warn('DOM property value: ' + this.bindingInput.nativeElement.value); + } + + working(): any { + console.warn('Test Button works!'); + } + + toggleDisabled(): any { + + let testButton = document.getElementById('testButton'); + testButton.disabled = !testButton.disabled; + console.warn(testButton.disabled); + } +} diff --git a/aio/content/examples/binding-syntax/src/app/app.module.ts b/aio/content/examples/binding-syntax/src/app/app.module.ts new file mode 100644 index 0000000000..926975afe8 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/app/app.module.ts @@ -0,0 +1,18 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; + + +import { AppComponent } from './app.component'; + + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/aio/content/examples/binding-syntax/src/index.html b/aio/content/examples/binding-syntax/src/index.html new file mode 100644 index 0000000000..fe53d5a822 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/index.html @@ -0,0 +1,14 @@ + + + + + + Angular binding syntax example + + + + + Loading... + + + diff --git a/aio/content/examples/binding-syntax/src/main.ts b/aio/content/examples/binding-syntax/src/main.ts new file mode 100644 index 0000000000..0740658908 --- /dev/null +++ b/aio/content/examples/binding-syntax/src/main.ts @@ -0,0 +1,12 @@ +// #docregion +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); diff --git a/aio/content/examples/binding-syntax/stackblitz.json b/aio/content/examples/binding-syntax/stackblitz.json new file mode 100644 index 0000000000..49fb8fc01a --- /dev/null +++ b/aio/content/examples/binding-syntax/stackblitz.json @@ -0,0 +1,10 @@ +{ + "description": "Binding Syntax", + "files": [ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[1,2].*" + ], + "file": "src/app/app.component.ts", + "tags": ["Binding Syntax"] +} diff --git a/aio/content/guide/template-syntax.md b/aio/content/guide/template-syntax.md index 852d000730..c77b1f0765 100644 --- a/aio/content/guide/template-syntax.md +++ b/aio/content/guide/template-syntax.md @@ -305,18 +305,21 @@ you're ready to learn about the varieties of data binding syntax beyond interpol {@a binding-syntax} -## Binding syntax: An overview +## Binding syntax: an overview -Data binding is a mechanism for coordinating what users see, with application data values. +Data-binding is a mechanism for coordinating what users see, specifically +with application data values. While you could push values to and pull values from HTML, -the application is easier to write, read, and maintain if you turn these chores over to a binding framework. -You simply declare bindings between binding sources and target HTML elements and let the framework do the work. +the application is easier to write, read, and maintain if you turn these tasks over to a binding framework. +You simply declare bindings between binding sources, target HTML elements, and let the framework do the rest. -Angular provides many kinds of data binding. -This guide covers most of them, after a high-level view of Angular data binding and its syntax. +For a demonstration of the syntax and code snippets in this section, see the binding syntax example. -Binding types can be grouped into three categories distinguished by the direction of data flow: -from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _view-to-source-to-view_: +Angular provides many kinds of data-binding. Binding types can be grouped into three categories distinguished by the direction of data flow: + +* From the _source-to-view_ +* From _view-to-source_ +* Two-way sequence: _view-to-source-to-view_