docs: rewrite attribute binding section and add example (#26004)
PR Close #26004
This commit is contained in:
parent
e76690be29
commit
b5aa0473fc
|
@ -0,0 +1,47 @@
|
|||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('Attribute binding example', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should display Property Binding with Angular', function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
|
||||
});
|
||||
|
||||
it('should display a table', function() {
|
||||
expect(element.all(by.css('table')).isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should display an Aria button', function () {
|
||||
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
|
||||
});
|
||||
|
||||
it('should display a blue background on div', function () {
|
||||
expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)');
|
||||
});
|
||||
|
||||
it('should display a blue div with a red border', function () {
|
||||
expect(element.all(by.css('div')).get(4).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||
});
|
||||
|
||||
it('should display a div with replaced classes', function () {
|
||||
expect(element.all(by.css('div')).get(5).getAttribute('class')).toEqual('new-class');
|
||||
});
|
||||
|
||||
it('should display four buttons', function() {
|
||||
let redButton = element.all(by.css('button')).get(1);
|
||||
let saveButton = element.all(by.css('button')).get(2);
|
||||
let bigButton = element.all(by.css('button')).get(3);
|
||||
let smallButton = element.all(by.css('button')).get(4);
|
||||
|
||||
expect(redButton.getCssValue('color')).toEqual('rgba(255, 0, 0, 1)');
|
||||
expect(saveButton.getCssValue('background-color')).toEqual('rgba(0, 255, 255, 1)');
|
||||
expect(bigButton.getText()).toBe('Big');
|
||||
expect(smallButton.getText()).toBe('Small');
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
.special {
|
||||
background-color: #1976d2;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-weight: bold;
|
||||
}
|
||||
.clearance {
|
||||
border: 2px solid #d41e2e;
|
||||
|
||||
}
|
||||
.item-clearance {
|
||||
font-style: italic;
|
||||
|
||||
}
|
||||
|
||||
.new-class {
|
||||
background-color: #ed1b2f;
|
||||
font-style: italic;
|
||||
color: #fff;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
<h1>Attribute, class, and style bindings</h1>
|
||||
<h2>Attribute binding</h2>
|
||||
<!-- #docregion attrib-binding-colspan -->
|
||||
<table border=1>
|
||||
<!-- expression calculates colspan=2 -->
|
||||
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
|
||||
|
||||
<!-- ERROR: There is no `colspan` property to set!
|
||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||
-->
|
||||
<!-- #docregion colSpan -->
|
||||
<!-- Notice the colSpan property is camel case -->
|
||||
<tr><td [colSpan]="1 + 1">Three-Four</td></tr>
|
||||
<!-- #enddocregion colSpan -->
|
||||
|
||||
<tr><td>Five</td><td>Six</td></tr>
|
||||
</table>
|
||||
<!-- #enddocregion attrib-binding-colspan -->
|
||||
|
||||
<div>
|
||||
<!-- #docregion attrib-binding-aria -->
|
||||
<!-- create and set an aria attribute for assistive technology -->
|
||||
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
|
||||
<!-- #enddocregion attrib-binding-aria -->
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Class binding</h2>
|
||||
|
||||
<!-- #docregion is-special -->
|
||||
<h3>toggle the "special" class on/off with a property:</h3>
|
||||
<div [class.special]="isSpecial">The class binding is special.</div>
|
||||
|
||||
<h3>binding to class.special overrides the class attribute:</h3>
|
||||
<div class="special" [class.special]="!isSpecial">This one is not so special.</div>
|
||||
|
||||
<h3>Using the bind- syntax:</h3>
|
||||
<div bind-class.special="isSpecial">This class binding is special too.</div>
|
||||
<!-- #enddocregion is-special -->
|
||||
|
||||
<!-- #docregion add-class -->
|
||||
<h3>Add a class:</h3>
|
||||
<div class="item clearance special" [class.item-clearance]="itemClearance">Add another class</div>
|
||||
<!-- #enddocregion add-class -->
|
||||
|
||||
<!-- #docregion class-override -->
|
||||
<h3>Overwrite all existing classes with a new class:</h3>
|
||||
<div class="item clearance special" [attr.class]="resetClasses">Reset all classes at once</div>
|
||||
<!-- #enddocregion class-override -->
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Style binding</h2>
|
||||
|
||||
<!-- #docregion style-binding-->
|
||||
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
|
||||
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
|
||||
<!-- #enddocregion style-binding -->
|
||||
|
||||
<!-- #docregion style-binding-condition-->
|
||||
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
|
||||
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
|
||||
<!-- #enddocregion style-binding-condition-->
|
|
@ -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,15 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
actionName = 'Go for it';
|
||||
isSpecial = true;
|
||||
itemClearance = true;
|
||||
resetClasses = 'new-class';
|
||||
canSave = true;
|
||||
|
||||
}
|
|
@ -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 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AttributeBinding</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>
|
|
@ -0,0 +1,12 @@
|
|||
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));
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"description": "Attribute Binding",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2].*"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["Attribute Binding"]
|
||||
}
|
|
@ -895,108 +895,103 @@ of the `evilTitle` examples.
|
|||
|
||||
## Attribute, class, and style bindings
|
||||
|
||||
The template syntax provides specialized one-way bindings for scenarios less well suited to property binding.
|
||||
The template syntax provides specialized one-way bindings for scenarios less well-suited to property binding.
|
||||
|
||||
To see attribute, class, and style bindings in a functioning app, see the <live-example name="attribute-binding"></live-example> especially for this section.
|
||||
|
||||
|
||||
### Attribute binding
|
||||
|
||||
You can set the value of an attribute directly with an **attribute binding**.
|
||||
Set the value of an attribute directly with an **attribute binding**. This is the only exception to the rule that a binding sets a target property and the only binding that creates and sets an attribute.
|
||||
|
||||
Usually, setting an element property with a [property binding](guide/template-syntax#property-binding)
|
||||
is preferable to setting the attribute with a string. However, sometimes
|
||||
there is no element property to bind, so attribute binding is the solution.
|
||||
|
||||
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG). They are purely attributes, don't correspond to element properties, and don't set element properties. In these cases, there are no property targets to bind to.
|
||||
|
||||
Attribute binding syntax resembles property binding, but
|
||||
instead of an element property between brackets, start with the prefix `attr`,
|
||||
followed by a dot (`.`), and the name of the attribute.
|
||||
You then set the attribute value, using an expression that resolves to a string,
|
||||
or remove the attribute when the expression resolves to `null`.
|
||||
|
||||
One of the primary use cases for attribute binding
|
||||
is to set ARIA attributes, as in this example:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="attrib-binding-aria" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
This is the only exception to the rule that a binding sets a target property.
|
||||
This is the only binding that creates and sets an attribute.
|
||||
#### `colspan` and `colSpan`
|
||||
|
||||
</div>
|
||||
Notice the difference between the `colspan` attribute and the `colSpan` property.
|
||||
|
||||
This guide stresses repeatedly that setting an element property with a property binding
|
||||
is always preferred to setting the attribute with a string. Why does Angular offer attribute binding?
|
||||
|
||||
**You must use attribute binding when there is no element property to bind.**
|
||||
|
||||
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA),
|
||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG), and
|
||||
table span attributes. They are pure attributes.
|
||||
They do not correspond to element properties, and they do not set element properties.
|
||||
There are no property targets to bind to.
|
||||
|
||||
This fact becomes obvious when you write something like this.
|
||||
If you wrote something like this:
|
||||
|
||||
<code-example language="html">
|
||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||
</code-example>
|
||||
|
||||
And you get this error:
|
||||
You'd get this error:
|
||||
|
||||
<code-example format="nocode">
|
||||
Template parse errors:
|
||||
Can't bind to 'colspan' since it isn't a known native property
|
||||
</code-example>
|
||||
|
||||
As the message says, the `<td>` element does not have a `colspan` property.
|
||||
It has the "colspan" *attribute*, but
|
||||
interpolation and property binding can set only *properties*, not attributes.
|
||||
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||||
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
||||
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
||||
|
||||
You need attribute bindings to create and bind to such attributes.
|
||||
Instead, you'd use property binding and write it like this:
|
||||
|
||||
Attribute binding syntax resembles property binding.
|
||||
Instead of an element property between brackets, start with the prefix **`attr`**,
|
||||
followed by a dot (`.`) and the name of the attribute.
|
||||
You then set the attribute value, using an expression that resolves to a string.
|
||||
|
||||
Bind `[attr.colspan]` to a calculated value:
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="attrib-binding-colspan" header="src/app/app.component.html" linenums="false">
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Here's how the table renders:
|
||||
|
||||
<table border="1px">
|
||||
<tr><td colspan="2">One-Two</td></tr>
|
||||
<tr><td>Five</td><td>Six</td></tr>
|
||||
</table>
|
||||
|
||||
One of the primary use cases for attribute binding
|
||||
is to set ARIA attributes, as in this example:
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="attrib-binding-aria" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
</div>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
### Class binding
|
||||
|
||||
You can add and remove CSS class names from an element's `class` attribute with
|
||||
Add and remove CSS class names from an element's `class` attribute with
|
||||
a **class binding**.
|
||||
|
||||
Class binding syntax resembles property binding.
|
||||
Instead of an element property between brackets, start with the prefix `class`,
|
||||
Here's how to set the attribute without binding in plain HTML:
|
||||
|
||||
```html
|
||||
<!-- standard class attribute setting -->
|
||||
<div class="item clearance special">Item clearance special</div>
|
||||
```
|
||||
|
||||
Class binding syntax resembles property binding, but instead of an element property between brackets, start with the prefix `class`,
|
||||
optionally followed by a dot (`.`) and the name of a CSS class: `[class.class-name]`.
|
||||
|
||||
The following examples show how to add and remove the application's "special" class
|
||||
with class bindings. Here's how to set the attribute without binding:
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-1" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-2" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="class-override" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can also add append a class to an element without overwriting the classes already on the element:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="add-class" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Finally, you can bind to a specific class name.
|
||||
Angular adds the class when the template expression evaluates to truthy.
|
||||
It removes the class when the expression is falsy.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="class-binding-3" header="src/app/app.component.html" linenums="false">
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="is-special" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
While this is a fine way to toggle a single class name,
|
||||
the [NgClass directive](guide/template-syntax#ngClass) is usually preferred when managing multiple class names at the same time.
|
||||
|
||||
</div>
|
||||
While this technique is suitable for toggling a single class name,
|
||||
consider the [`NgClass`](guide/template-syntax#ngClass) directive when
|
||||
managing multiple class names at the same time.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
@ -1009,21 +1004,17 @@ Style binding syntax resembles property binding.
|
|||
Instead of an element property between brackets, start with the prefix `style`,
|
||||
followed by a dot (`.`) and the name of a CSS style property: `[style.style-property]`.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-1" header="src/app/app.component.html" linenums="false">
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Some style binding styles have a unit extension.
|
||||
The following example conditionally sets the font size in “em” and “%” units .
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="style-binding-2" header="src/app/app.component.html" linenums="false">
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
While this is a fine way to set a single style,
|
||||
the [NgStyle directive](guide/template-syntax#ngStyle) is generally preferred when setting several inline styles at the same time.
|
||||
|
||||
</div>
|
||||
**This technique is suitable for setting a single style, but consider
|
||||
the [`NgStyle`](guide/template-syntax#ngStyle) directive when setting several inline styles at the same time.**
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
|
Loading…
Reference in New Issue