docs: rewrite binding-syntax section in template-syntax.md (#25561)
PR Close #25561
This commit is contained in:
parent
48d11d5fa0
commit
bb4e230eae
|
@ -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/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/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/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/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
|
/aio/content/examples/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
div {
|
||||||
|
padding: .25rem 0;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>Binding syntax</h1>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Button disabled state bound to isUnchanged property</h2>
|
||||||
|
<!-- #docregion disabled-button -->
|
||||||
|
<!-- Bind button disabled state to `isUnchanged` property -->
|
||||||
|
<button [disabled]="isUnchanged">Save</button>
|
||||||
|
<!-- #enddocregion disabled-button -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div (keyup)="0">
|
||||||
|
<h2>HTML attributes and DOM properties</h2>
|
||||||
|
<p>1. Use the inspector to see the HTML attribute and DOM property values. Click the buttons to log values to the console.</p>
|
||||||
|
|
||||||
|
<label>HTML Attribute Initializes to "Sarah":
|
||||||
|
<input type="text" value="Sarah" #bindingInput></label>
|
||||||
|
<div>
|
||||||
|
<button (click)="getHTMLAttributeValue()">Get HTML attribute value</button> Won't change.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button (click)="getDOMPropertyValue()">Get DOM property value</button> Changeable. Angular works with these.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>2. Change the name in the input and click the buttons again.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Disabled property vs. attribute</h3>
|
||||||
|
<p>Use the inspector to see the Test Button work and its disabled property toggle.</p>
|
||||||
|
<div>
|
||||||
|
<button id="testButton" (click)="working()">Test Button</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button (click)="toggleDisabled()">Toggle disabled property for Test Button</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
|
@ -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!');
|
||||||
|
}));
|
||||||
|
});
|
|
@ -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 = <HTMLInputElement> document.getElementById('testButton');
|
||||||
|
testButton.disabled = !testButton.disabled;
|
||||||
|
console.warn(testButton.disabled);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 { }
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!-- #docregion -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<title>Angular binding syntax example</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root>Loading...</app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<!-- #enddocregion -->
|
|
@ -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);
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"description": "Binding Syntax",
|
||||||
|
"files": [
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[1,2].*"
|
||||||
|
],
|
||||||
|
"file": "src/app/app.component.ts",
|
||||||
|
"tags": ["Binding Syntax"]
|
||||||
|
}
|
|
@ -305,18 +305,21 @@ you're ready to learn about the varieties of data binding syntax beyond interpol
|
||||||
|
|
||||||
{@a binding-syntax}
|
{@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,
|
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.
|
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 and target HTML elements and let the framework do the work.
|
You simply declare bindings between binding sources, target HTML elements, and let the framework do the rest.
|
||||||
|
|
||||||
Angular provides many kinds of data binding.
|
For a demonstration of the syntax and code snippets in this section, see the <live-example name="binding-syntax">binding syntax example</live-example>.
|
||||||
This guide covers most of them, after a high-level view of Angular data binding and its syntax.
|
|
||||||
|
|
||||||
Binding types can be grouped into three categories distinguished by the direction of data flow:
|
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_, and in the two-way sequence: _view-to-source-to-view_:
|
|
||||||
|
* From the _source-to-view_
|
||||||
|
* From _view-to-source_
|
||||||
|
* Two-way sequence: _view-to-source-to-view_
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
td, th {vertical-align: top}
|
td, th {vertical-align: top}
|
||||||
|
@ -331,19 +334,23 @@ from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _
|
||||||
</col>
|
</col>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Data direction
|
Type
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Syntax
|
Syntax
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Type
|
Category
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
One-way<br>from data source<br>to view target
|
Interpolation<br>
|
||||||
|
Property<br>
|
||||||
|
Attribute<br>
|
||||||
|
Class<br>
|
||||||
|
Style
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
@ -354,16 +361,13 @@ from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
Interpolation<br>
|
One-way<br>from data source<br>to view target
|
||||||
Property<br>
|
|
||||||
Attribute<br>
|
|
||||||
Class<br>
|
|
||||||
Style
|
|
||||||
</td>
|
</td>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
One-way<br>from view target<br>to data source
|
Event
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example>
|
<code-example>
|
||||||
|
@ -371,8 +375,9 @@ from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _
|
||||||
on-target="statement"
|
on-target="statement"
|
||||||
</code-example>
|
</code-example>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
Event
|
One-way<br>from view target<br>to data source
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -392,118 +397,138 @@ from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Binding types other than interpolation have a **target name** to the left of the equal sign,
|
Binding types other than interpolation have a **target name** to the left of the equal sign, either surrounded by punctuation, `[]` or `()`,
|
||||||
either surrounded by punctuation (`[]`, `()`) or preceded by a prefix (`bind-`, `on-`, `bindon-`).
|
or preceded by a prefix: `bind-`, `on-`, `bindon-`.
|
||||||
|
|
||||||
The target name is the name of a _property_. It may look like the name of an _attribute_ but it never is.
|
The *target* of a binding is the property or event inside the binding punctuation: `[]`, `()` or `[()]`.
|
||||||
To appreciate the difference, you must develop a new way to think about template HTML.
|
|
||||||
|
|
||||||
### A new mental model
|
Every public member of a **source** directive is automatically available for binding.
|
||||||
|
You don't have to do anything special to access a directive member in a template expression or statement.
|
||||||
|
|
||||||
With all the power of data binding and the ability to extend the HTML vocabulary
|
|
||||||
with custom markup, it is tempting to think of template HTML as *HTML Plus*.
|
|
||||||
|
|
||||||
It really *is* HTML Plus.
|
## Data-binding and HTML
|
||||||
But it's also significantly different than the HTML you're used to.
|
|
||||||
It requires a new mental model.
|
|
||||||
|
|
||||||
In the normal course of HTML development, you create a visual structure with HTML elements, and
|
In the normal course of HTML development, you create a visual structure with HTML elements, and
|
||||||
you modify those elements by setting element attributes with string constants.
|
you modify those elements by setting element attributes with string constants.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="img+button" header="src/app/app.component.html" linenums="false">
|
```html
|
||||||
|
<div class="special">Plain old HTML</div>
|
||||||
|
<img src="images/item.png">
|
||||||
|
<button disabled>Save</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
With data-binding, you can control things like the state of a button:
|
||||||
|
|
||||||
|
<code-example path="binding-syntax/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
You still create a structure and initialize attribute values this way in Angular templates.
|
Notice that the binding is to the `disabled` property of the button's DOM element,
|
||||||
|
**not** the attribute. This applies to data-binding in general. Data-binding works with *properties* of DOM elements, components, and directives, not HTML *attributes*.
|
||||||
|
|
||||||
Then you learn to create new elements with components that encapsulate HTML
|
|
||||||
and drop them into templates as if they were native HTML elements.
|
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="hero-detail-1" header="src/app/app.component.html" linenums="false">
|
## HTML attribute vs. DOM property
|
||||||
</code-example>
|
|
||||||
|
|
||||||
That's HTML Plus.
|
The distinction between an HTML attribute and a DOM property is key to understanding
|
||||||
|
how Angular binding works. **Attributes are defined by HTML. Properties are accessed from DOM, or the Document Object Model, nodes.**
|
||||||
|
|
||||||
Then you learn about data binding. The first binding you meet might look like this:
|
* A few HTML attributes have 1:1 mapping to properties; for example, `id`.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="disabled-button-1" header="src/app/app.component.html" linenums="false">
|
* Some HTML attributes don't have corresponding properties; for example, `aria-*`.
|
||||||
</code-example>
|
|
||||||
|
|
||||||
You'll get to that peculiar bracket notation in a moment. Looking beyond it,
|
* Some DOM properties don't have corresponding attributes; for example, `textContent`.
|
||||||
your intuition suggests that you're binding to the button's `disabled` attribute and setting
|
|
||||||
it to the current value of the component's `isUnchanged` property.
|
|
||||||
|
|
||||||
Your intuition is incorrect! Your everyday HTML mental model is misleading.
|
This general rule can help you build a mental model of attributes and DOM properties:
|
||||||
In fact, once you start data binding, you are no longer working with HTML *attributes*. You aren't setting attributes.
|
**attributes initialize DOM properties and then they are done.
|
||||||
You are setting the *properties* of DOM elements, components, and directives.
|
Property values can change; attribute values can't.**
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
### HTML attribute vs. DOM property
|
There is, of course, an exception to this rule because attributes can be changed by `setAttribute()`, which will re-initialize corresponding DOM properties again.
|
||||||
|
|
||||||
The distinction between an HTML attribute and a DOM property is crucial to understanding how Angular binding works.
|
</div>
|
||||||
|
|
||||||
**Attributes are defined by HTML. Properties are defined by the DOM (Document Object Model).**
|
Comparing the [`<td>` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
|
||||||
|
attributes to the [`<td>` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement)
|
||||||
* A few HTML attributes have 1:1 mapping to properties. `id` is one example.
|
provides a helpful
|
||||||
|
example for differentiation. In particular, you can navigate from the attributes
|
||||||
* Some HTML attributes don't have corresponding properties. `colspan` is one example.
|
page to the properties via "DOM interface" link, and navigate the inheritance
|
||||||
|
hierarchy up to `HTMLTableCellElement`.
|
||||||
* Some DOM properties don't have corresponding attributes. `textContent` is one example.
|
|
||||||
|
|
||||||
* Many HTML attributes appear to map to properties ... but not in the way you might think!
|
|
||||||
|
|
||||||
That last category is confusing until you grasp this general rule:
|
|
||||||
|
|
||||||
**Attributes *initialize* DOM properties and then they are done.
|
|
||||||
Property values can change; attribute values can't.**
|
|
||||||
|
|
||||||
For example, when the browser renders `<input type="text" value="Bob">`, it creates a
|
|
||||||
corresponding DOM node with a `value` property *initialized* to "Bob".
|
|
||||||
|
|
||||||
When the user enters "Sally" into the input box, the DOM element `value` *property* becomes "Sally".
|
|
||||||
But the HTML `value` *attribute* remains unchanged as you discover if you ask the input element
|
|
||||||
about that attribute: `input.getAttribute('value')` returns "Bob".
|
|
||||||
|
|
||||||
The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value.
|
|
||||||
|
|
||||||
The `disabled` attribute is another peculiar example. A button's `disabled` *property* is
|
|
||||||
`false` by default so the button is enabled.
|
|
||||||
When you add the `disabled` *attribute*, its presence alone initializes the button's `disabled` *property* to `true`
|
|
||||||
so the button is disabled.
|
|
||||||
|
|
||||||
Adding and removing the `disabled` *attribute* disables and enables the button. The value of the *attribute* is irrelevant,
|
|
||||||
which is why you cannot enable a button by writing `<button disabled="false">Still Disabled</button>`.
|
|
||||||
|
|
||||||
Setting the button's `disabled` *property* (say, with an Angular binding) disables or enables the button.
|
|
||||||
The value of the *property* matters.
|
|
||||||
|
|
||||||
**The HTML attribute and the DOM property are not the same thing, even when they have the same name.**
|
**The HTML attribute and the DOM property are not the same thing, even when they have the same name.**
|
||||||
|
|
||||||
|
For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example 1: an `<input>`
|
||||||
|
|
||||||
|
When the browser renders `<input type="text" value="Sarah">`, it creates a
|
||||||
|
corresponding DOM node with a `value` property initialized to "Sarah".
|
||||||
|
|
||||||
|
```html
|
||||||
|
<input type="text" value="Sarah">
|
||||||
|
```
|
||||||
|
|
||||||
|
When the user enters "Sally" into the `<input>`, the DOM element `value` *property* becomes "Sally".
|
||||||
|
However, if you look at the HTML attribute `value` using `input.getAttribute('value')`, you can see that the *attribute* remains unchanged—it returns "Sarah".
|
||||||
|
|
||||||
|
The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value.
|
||||||
|
|
||||||
|
To see attributes versus DOM properties in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax.
|
||||||
|
|
||||||
|
### Example 2: a disabled button
|
||||||
|
|
||||||
|
The `disabled` attribute is another example. A button's `disabled`
|
||||||
|
*property* is `false` by default so the button is enabled.
|
||||||
|
|
||||||
|
When you add the `disabled` *attribute*, its presence alone
|
||||||
|
initializes the button's `disabled` *property* to `true`
|
||||||
|
so the button is disabled.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button disabled>Test Button</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding and removing the `disabled` *attribute* disables and
|
||||||
|
enables the button.
|
||||||
|
However, the value of the *attribute* is irrelevant,
|
||||||
|
which is why you cannot enable a button by writing `<button disabled="false">Still Disabled</button>`.
|
||||||
|
|
||||||
|
To control the state of the button, set the `disabled` *property*,
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
**Note:** Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<input [disabled]="condition ? true : false">
|
||||||
|
<input [attr.disabled]="condition ? 'disabled' : null">
|
||||||
|
```
|
||||||
|
|
||||||
|
Generally, use property binding over attribute binding as it is more intuitive (being a boolean value), has a shorter syntax, and is more performant.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
This fact bears repeating:
|
**The HTML attribute and the DOM property are different things, even when they have the same name.**
|
||||||
|
|
||||||
**Template binding works with *properties* and *events*, not *attributes*.**
|
**Template binding works with *properties* and *events*, not *attributes*.**
|
||||||
|
|
||||||
<div class="callout is-helpful">
|
To see the `disabled` button example in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax. This example shows you how to toggle the disabled property from the component.
|
||||||
|
|
||||||
<header>
|
|
||||||
A world without attributes
|
|
||||||
</header>
|
|
||||||
|
|
||||||
In the world of Angular, the only role of attributes is to initialize element and directive state.
|
### Angular and attributes
|
||||||
When you write a data binding, you're dealing exclusively with properties and events of the target object.
|
|
||||||
HTML attributes effectively disappear.
|
|
||||||
|
|
||||||
</div>
|
In Angular, the only role of attributes is to initialize element and directive state.
|
||||||
|
When you write a data-binding, you're dealing exclusively with properties and events of the target object.
|
||||||
|
|
||||||
With this model firmly in mind, read on to learn about binding targets.
|
|
||||||
|
|
||||||
### Binding targets
|
## Binding targets
|
||||||
|
|
||||||
The **target of a data binding** is something in the DOM.
|
The **target of a data-binding** is something in the DOM.
|
||||||
Depending on the binding type, the target can be an
|
Depending on the binding type, the target can be a
|
||||||
(element | component | directive) property, an
|
property (element, component, or directive), an
|
||||||
(element | component | directive) event, or (rarely) an attribute name.
|
event (element, component, or directive), or sometimes an attribute name.
|
||||||
The following table summarizes:
|
The following table summarizes:
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -538,8 +563,10 @@ The following table summarizes:
|
||||||
Directive property
|
Directive property
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code>src</code>, <code>hero</code>, and <code>ngClass</code> in the following:
|
||||||
|
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
<!-- For more information, see [Property Binding](guide/property-binding). -->
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -552,8 +579,11 @@ The following table summarizes:
|
||||||
Directive event
|
Directive event
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="event-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code>click</code>, <code>deleteRequest</code>, and <code>myClick</code> in the following:
|
||||||
|
<code-example path="template-syntax/src/app/app.component.html" region="event-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
<!-- KW--Why don't these links work in the table? -->
|
||||||
|
<!-- <div>For more information, see [Event Binding](guide/event-binding).</div> -->
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -564,7 +594,7 @@ The following table summarizes:
|
||||||
Event and property
|
Event and property
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="2-way-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="2-way-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -577,7 +607,7 @@ The following table summarizes:
|
||||||
(the exception)
|
(the exception)
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="attribute-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="attribute-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -589,7 +619,7 @@ The following table summarizes:
|
||||||
<code>class</code> property
|
<code>class</code> property
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -601,13 +631,13 @@ The following table summarizes:
|
||||||
<code>style</code> property
|
<code>style</code> property
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-syntax-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-syntax-1" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
With this broad view in mind, you're ready to look at binding types in detail.
|
<!-- end of binding syntax -->
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue