Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: edit template ref vars copy in Template Syntax and example #27371

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict'; // necessary for es6 output in node

import { browser, element, by } from 'protractor';
import { logging } from 'selenium-webdriver';

describe('Template-reference-variables-example', function() {
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 Template reference variables', function() {
expect(element(by.css('h1')).getText()).toEqual(
'Template reference variables'
);
});

it('should log a Calling 123 ... message', async () => {
let callButton = element.all(by.css('button')).get(0);
let phoneInput = element.all(by.css('input')).get(0);
await phoneInput.sendKeys('123');
await callButton.click();
const contents = 'Calling 123 ...';
await logChecker(callButton, contents);
});

it('should log a Faxing 123 ... message', async () => {
let faxButton = element.all(by.css('button')).get(1);
let faxInput = element.all(by.css('input')).get(1);
await faxInput.sendKeys('123');
await faxButton.click();
const contents = 'Faxing 123 ...';
await logChecker(faxButton, contents);
});

it('should display a disabled button', function() {
let disabledButton = element.all(by.css('button')).get(2);
expect(disabledButton.isEnabled()).toBe(false);
});

it('should submit form', async () => {
let submitButton = element.all(by.css('button')).get(3);
let nameInput = element.all(by.css('input')).get(2);
await nameInput.sendKeys('123');
await submitButton.click();
expect(element.all(by.css('div > p')).get(2).getText()).toEqual('Submitted. Form value is {"name":"123"}');
});


});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<h1>Template reference variables</h1>

<div>
<h2>Pass value to an event handler</h2>
<p>See console for output.</p>
<!-- #docregion ref-phone -->
<!-- #docregion ref-var -->
<input #phone placeholder="phone number" />
<!-- #enddocregion ref-var -->

<!-- lots of other elements -->

<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
<!-- #enddocregion ref-phone -->
</div>

<div>
<!-- #docregion ref-fax -->
<input ref-fax placeholder="fax number" />
<button (click)="callFax(fax.value)">Fax</button>
<!-- #enddocregion ref-fax -->
</div>

<hr />

<div>
<h2>Template reference variable with disabled button</h2>
<p>btn refers to the button element.</p>
<button
#btn
disabled
[innerHTML]="'disabled by attribute: ' + btn.disabled"
></button>
</div>

<hr />

<h2>Reference variables, forms, and NgForm</h2>
<!-- #docregion ngForm -->
<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
<label for="name"
>Name <input class="form-control" name="name" ngModel required />
</label>
<button type="submit">Submit</button>
</form>

<div [hidden]="!itemForm.form.valid">
<p>{{ submitMessage }}</p>
</div>

<!-- #enddocregion ngForm -->


<p>JSON: {{ itemForm.form.value | json }}</p>
Original file line number Diff line number Diff line change
@@ -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!');
}));
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';


@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('itemForm', { static: false }) form: NgForm;

private _submitMessage = '';

get submitMessage() {
return this._submitMessage;
}

onSubmit(form: NgForm) {
this._submitMessage = 'Submitted. Form value is ' + JSON.stringify(form.value);
}

callPhone(value: string) {
console.warn(`Calling ${value} ...`);
}

callFax(value: string) {
console.warn(`Faxing ${value} ...`);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { FormsModule } from '@angular/forms';



import { AppComponent } from './app.component';


@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
14 changes: 14 additions & 0 deletions aio/content/examples/template-reference-variables/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Template Reference Variables Example</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>Loading...</app-root>
</body>
</html>
11 changes: 11 additions & 0 deletions aio/content/examples/template-reference-variables/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Template Reference Variables",
"files": ["!**/*.d.ts", "!**/*.js", "!**/*.[1,2].*"],
"file": "src/app/app.component.ts",
"tags": ["Template Reference Variables"]
}
71 changes: 37 additions & 34 deletions aio/content/guide/template-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -1631,70 +1631,73 @@ For example, you could replace the `<confused-hero>` switch case with the follow

{@a template-reference-variable}

{@a template-reference-variables--var-}

{@a ref-vars}

{@a ref-var}

## Template reference variables ( <span class="syntax">#var</span> )
## Template reference variables (`#var`)

A **template reference variable** is often a reference to a DOM element within a template.
It can also be a reference to an Angular component or directive or a
<a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" title="MDN: Web Components">web component</a>.
It can also refer to a directive (which contains a component), an element, [TemplateRef](api/core/TemplateRef), or a <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" title="MDN: Web Components">web component</a>.

For a demonstration of the syntax and code snippets in this section, see the <live-example name="template-reference-variables">template reference variables example</live-example>.


Use the hash symbol (#) to declare a reference variable.
The `#phone` declares a `phone` variable on an `<input>` element.
The following reference variable, `#phone`, declares a `phone` variable on an `<input>` element.

<code-example path="template-syntax/src/app/app.component.html" region="ref-var" header="src/app/app.component.html" linenums="false">
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-var" header="src/app/app.component.html" linenums="false">
</code-example>

You can refer to a template reference variable _anywhere_ in the template.
The `phone` variable declared on this `<input>` is
consumed in a `<button>` on the other side of the template
You can refer to a template reference variable anywhere in the component's template.
Here, a `<button>` further down the template refers to the `phone` variable.

<code-example path="template-syntax/src/app/app.component.html" region="ref-phone" header="src/app/app.component.html" linenums="false">
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-phone" header="src/app/app.component.html" linenums="false">
</code-example>

<h3 class="no-toc">How a reference variable gets its value</h3>

In most cases, Angular sets the reference variable's value to the element on which it was declared.
In the previous example, `phone` refers to the _phone number_ `<input>` box.
The phone button click handler passes the _input_ value to the component's `callPhone` method.
But a directive can change that behavior and set the value to something else, such as itself.
The `NgForm` directive does that.
In most cases, Angular sets the reference variable's value to the element on which it is declared.
In the previous example, `phone` refers to the phone number `<input>`.
The button's click handler passes the `<input>` value to the component's `callPhone()` method.

The following is a *simplified* version of the form example in the [Forms](guide/forms) guide.
The `NgForm` directive can change that behavior and set the value to something else. In the following example, the template reference variable, `itemForm`, appears three times separated
by HTML.

<code-example path="template-syntax/src/app/hero-form.component.html" header="src/app/hero-form.component.html" linenums="false">
<code-example path="template-reference-variables/src/app/app.component.html" region="ngForm" header="src/app/hero-form.component.html" linenums="false">
</code-example>

A template reference variable, `heroForm`, appears three times in this example, separated
by a large amount of HTML.
What is the value of `heroForm`?
The reference value of itemForm, without the ngForm attribute value, would be
the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement).
kapunahelewong marked this conversation as resolved.
Show resolved Hide resolved
There is, however, a difference between a Component and a Directive in that a `Component
`will be referenced without specifying the attribute value, and a `Directive` will not
change the implicit reference (that is, the element).


If Angular hadn't taken it over when you imported the `FormsModule`,
it would be the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement).
The `heroForm` is actually a reference to an Angular [NgForm](api/forms/NgForm "API: NgForm")

However, with `NgForm`, `itemForm` is a reference to the [NgForm](api/forms/NgForm "API: NgForm")
directive with the ability to track the value and validity of every control in the form.

The native `<form>` element doesn't have a `form` property.
But the `NgForm` directive does, which explains how you can disable the submit button
if the `heroForm.form.valid` is invalid and pass the entire form control tree
to the parent component's `onSubmit` method.
The native `<form>` element doesn't have a `form` property, but the `NgForm` directive does, which allows disabling the submit button
if the `itemForm.form.valid` is invalid and passing the entire form control tree
to the parent component's `onSubmit()` method.

<h3 class="no-toc">Template reference variable considerations</h3>

<h3 class="no-toc">Template reference variable warning notes</h3>
A template _reference_ variable (`#phone`) is not the same as a template _input_ variable (`let phone`) such as in an [`*ngFor`](guide/template-syntax#template-input-variable).
See [_Structural Directives_](guide/structural-directives#template-input-variable) for more information.

A template _reference_ variable (`#phone`) is _not_ the same as a template _input_ variable (`let phone`)
such as you might see in an [`*ngFor`](guide/template-syntax#template-input-variable).
Learn the difference in the [_Structural Directives_](guide/structural-directives#template-input-variable) guide.
The scope of a reference variable is the entire template. So, don't define the same variable name more than once in the same template as the runtime value will be unpredictable.

The scope of a reference variable is the _entire template_.
Do not define the same variable name more than once in the same template.
The runtime value will be unpredictable.
#### Alternative syntax

You can use the `ref-` prefix alternative to `#`.
This example declares the `fax` variable as `ref-fax` instead of `#fax`.

<code-example path="template-syntax/src/app/app.component.html" region="ref-fax" header="src/app/app.component.html" linenums="false">

<code-example path="template-reference-variables/src/app/app.component.html" region="ref-fax" header="src/app/app.component.html" linenums="false">
</code-example>


Expand Down