Angular2 - 组件变量/组件上的双向数据绑定class 属性?
Angular2 - two way databinding on a component variable / component class property?
在 Angular2 (Beta 6) 中,我有一个主菜单组件。
<mainmenu></mainmenu>
我想为宽或窄绑定一个布尔值。所以我把它变成了:
<mainmenu [(menuvisible)]="true"></mainmenu>
但我想要的(我认为)是绑定到一个 javascript class 属性 (因为我可能还有其他东西要绑定但想通过使用一个来保持整洁组件中的单个 class)。
我收到一个错误
EXCEPTION: Template parse errors: Invalid property name
'menumodel.visible' ("
][(menumodel.visible)]="menumodel.visible">
如果我用单个变量而不是 class 尝试相同的方法,我得到:
Template parse errors: Parser Error: Unexpected token '='
然而,这(单向绑定?)似乎确实有效(但我可能想触发菜单从另一个组件转到 wide/narrow,所以觉得这应该是一个 two-way 数据绑定 属性):
<menu [vis]="true"></menu>
这是我的菜单组件的一部分:
@Component({
selector: 'menu',
templateUrl: './app/menu.html',
providers: [HTTP_PROVIDERS, ApplicationService],
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgForm]
})
export class MenuComponent implements OnInit {
mainmenu: MainMenuVM;
constructor(private _applicationService: ApplicationService) {
this.mainmenu = new MainMenuVM();
}
// ...ngOnInit, various functions
}
这是我的 MainMenu 视图模型 class
export class MainMenuVM {
public visible: boolean;
constructor(
) { this.visible = true; }
}
我正在尝试创建一个包含图标和文本的菜单,但可以缩小到只显示图标。我会将此事件向上发送到 parent 组件,以更改菜单旁边容器的位置。触发内容容器最大化将触发菜单变窄 - 我并不是说这是最好的方法,但我想在深入之前解决这个特定问题。
请注意:我在这里没有对输入控件进行数据绑定 - 只是对组件进行数据绑定,这样我就可以修改 UI.
这是来自 Angular 备忘单
<my-cmp [(title)]="name">
Sets up two-way data binding. Equivalent to: <my-cmp [title]="name" (titleChange)="name=$event">
提前致谢!
更新
整合已接受答案中的代码并在此处针对我的特定用例进行调整,最终工作代码:
app.html
...header html content
// This is what I started with
<!--<menu [menuvisible]="true" (menuvisibleChange)="menuvisible=$event"></menu>-->
// This is two way data binding
// 1. Banana-in-a-box is the input parameter
// 2. Banana-in-a-box is also the output parameter name (Angular appends it's usage with Change in code - to follow shortly)
// 3. Banana-in-a-box is the short hand way to declare the commented out code
// 4. First parameter (BIAB) refers to the child component, the second refers the variable it will store the result into.
// 5. If you just need an input use the remmed out code with just the first attribute / value
<menu [(menuvisible)]="menuvisible"></menu>
.. div content start
<router-outlet></router-outlet>
.. div content end
app.component.ts(根)
export class AppComponent implements OnInit{
menuvisible: Boolean;
}
menu.component.ts(根的child)
export class MenuComponent implements OnInit {
// Parameters - notice the appending of "Change"
@Input() menuvisible: boolean;
@Output() menuvisibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();
// Init
ngOnInit() {
// Populate menu - fetch application list
this.getApplications();
// Initially we want to show/hide the menu depending on the input parameter
(this.menuvisible === true) ? this.showMenu() : this.hideMenu();
}
//...more code
}
menu.html
<div id="menu" [ngClass]="menuStateClass" style="position: absolute; top:0px; left: 0px;z-index: 800; height: 100%; color: #fff; background-color: #282d32">
<div style="margin-top: 35px; padding: 5px 0px 5px 0px;">
<ul class="menuList" style="overflow-x: hidden;">
<li>IsMenuVisible:{{menuvisible}}</li>
<li style="border-bottom: 1px solid #3d4247"><a (click)="toggleMenu()"><i class="fa fa-bars menuIcon" style="color: white; font-size: 16px;"></i></a></li>
<li *ngFor="#app of applications">
<a [routerLink]="[app.routerLink]">
<i class="menuIcon" [ngClass]="app.icon" [style.color]="app.iconColour" style="color: white;"></i>
<span [hidden]="menuStateTextHidden">{{ app.name }}</span>
</a>
</li>
</ul>
</div>
</div>
记得导入你需要的东西,例如
import {Component, EventEmitter, OnInit, Input, Output} from
'angular2/core';
强烈推荐 YouTube 上的这个视频:
Angular 2 Tutorial (2016) - Inputs and Outputs
对于双向绑定,您需要类似的东西:
@Component({
selector: 'menu',
template: `
<button (click)="menuvisible = !menuvisible; menuvisibleChange.emit(menuvisible)">toggle</button>
<!-- or
<button (click)="toggleVisible()">toggle</button> -->
`,
// HTTP_PROVIDERS should now be imports: [HttpModule] in @NgModule()
providers: [/*HTTP_PROVIDERS*/, ApplicationService],
// This should now be added to declarations and imports in @NgModule()
// imports: [RouterModule, CommonModule, FormsModule]
directives: [/*ROUTER_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgForm*/]
})
export class MenuComponent implements OnInit {
@Input() menuvisible:boolean;
@Output() menuvisibleChange:EventEmitter<boolean> = new EventEmitter<boolean>();
// toggleVisible() {
// this.menuvisible = !this.menuvisible;
// this.menuvisibleChange.emit(this.menuvisible);
// }
}
并像
一样使用它
@Component({
selector: 'some-component',
template: `
<menu [(menuvisible)]="menuVisibleInParent"></menu>
<div>visible: {{menuVisibleInParent}}</div>
`
directives: [MenuComponent]
})
class SomeComponent {
menuVisibleInParent: boolean;
}
我创建了一个简短的 plunkr。
ngModel Like Two-Way-Databinding for components
您至少有两种可能性来为组件创建双向数据绑定
V1:使用 ngModel Like Syntax,您必须在 @Output 属性 的末尾创建一个与 @Input 属性 + "Change" 同名的行输出属性 姓名
@Input() name : string;
@Output() nameChange = new EventEmitter<string>();
对于 V1,您现在可以使用 ngModel 语法绑定到子组件
[(name)]="firstname"
V2。只需创建一个 @Input 和 @Output 属性 并使用您喜欢的命名方式
@Input() age : string;
@Output() ageChanged = new EventEmitter<string>();
对于 V2,您必须创建两个属性才能获得双向数据绑定
[age]="alter" (ageChanged)="alter = $event"
父组件
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<p>V1 Parentvalue Name: "{{firstname}}"<br/><input [(ngModel)]="firstname" > <br/><br/>
V2 Parentvalue Age: "{{alter}}" <br/><input [(ngModel)]="alter"> <br/><br/>
<my-child [(name)]="firstname" [age]="alter" (ageChanged)="alter = $event"></my-child></p>`
})
export class AppComponent {
firstname = 'Angular';
alter = "18";
}
子组件
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'my-child',
template: `<p>V1 Childvalue Name: "{{name}}"<br/><input [(ngModel)]="name" (keyup)="onNameChanged()"> <br/><br/>
<p>V2 Childvalue Age: "{{age}}"<br/><input [(ngModel)]="age" (keyup)="onAgeChanged()"> <br/></p>`
})
export class ChildComponent {
@Input() name : string;
@Output() nameChange = new EventEmitter<string>();
@Input() age : string;
@Output() ageChanged = new EventEmitter<string>();
public onNameChanged() {
this.nameChange.emit(this.name);
}
public onAgeChanged() {
this.ageChanged.emit(this.age);
}
}
在 Angular2 (Beta 6) 中,我有一个主菜单组件。
<mainmenu></mainmenu>
我想为宽或窄绑定一个布尔值。所以我把它变成了:
<mainmenu [(menuvisible)]="true"></mainmenu>
但我想要的(我认为)是绑定到一个 javascript class 属性 (因为我可能还有其他东西要绑定但想通过使用一个来保持整洁组件中的单个 class)。
我收到一个错误
EXCEPTION: Template parse errors: Invalid property name 'menumodel.visible' ("
][(menumodel.visible)]="menumodel.visible">
如果我用单个变量而不是 class 尝试相同的方法,我得到:
Template parse errors: Parser Error: Unexpected token '='
然而,这(单向绑定?)似乎确实有效(但我可能想触发菜单从另一个组件转到 wide/narrow,所以觉得这应该是一个 two-way 数据绑定 属性):
<menu [vis]="true"></menu>
这是我的菜单组件的一部分:
@Component({
selector: 'menu',
templateUrl: './app/menu.html',
providers: [HTTP_PROVIDERS, ApplicationService],
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgForm]
})
export class MenuComponent implements OnInit {
mainmenu: MainMenuVM;
constructor(private _applicationService: ApplicationService) {
this.mainmenu = new MainMenuVM();
}
// ...ngOnInit, various functions
}
这是我的 MainMenu 视图模型 class
export class MainMenuVM {
public visible: boolean;
constructor(
) { this.visible = true; }
}
我正在尝试创建一个包含图标和文本的菜单,但可以缩小到只显示图标。我会将此事件向上发送到 parent 组件,以更改菜单旁边容器的位置。触发内容容器最大化将触发菜单变窄 - 我并不是说这是最好的方法,但我想在深入之前解决这个特定问题。
请注意:我在这里没有对输入控件进行数据绑定 - 只是对组件进行数据绑定,这样我就可以修改 UI.
这是来自 Angular 备忘单
<my-cmp [(title)]="name">
Sets up two-way data binding. Equivalent to: <my-cmp [title]="name" (titleChange)="name=$event">
提前致谢!
更新
整合已接受答案中的代码并在此处针对我的特定用例进行调整,最终工作代码:
app.html
...header html content
// This is what I started with
<!--<menu [menuvisible]="true" (menuvisibleChange)="menuvisible=$event"></menu>-->
// This is two way data binding
// 1. Banana-in-a-box is the input parameter
// 2. Banana-in-a-box is also the output parameter name (Angular appends it's usage with Change in code - to follow shortly)
// 3. Banana-in-a-box is the short hand way to declare the commented out code
// 4. First parameter (BIAB) refers to the child component, the second refers the variable it will store the result into.
// 5. If you just need an input use the remmed out code with just the first attribute / value
<menu [(menuvisible)]="menuvisible"></menu>
.. div content start
<router-outlet></router-outlet>
.. div content end
app.component.ts(根)
export class AppComponent implements OnInit{
menuvisible: Boolean;
}
menu.component.ts(根的child)
export class MenuComponent implements OnInit {
// Parameters - notice the appending of "Change"
@Input() menuvisible: boolean;
@Output() menuvisibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();
// Init
ngOnInit() {
// Populate menu - fetch application list
this.getApplications();
// Initially we want to show/hide the menu depending on the input parameter
(this.menuvisible === true) ? this.showMenu() : this.hideMenu();
}
//...more code
}
menu.html
<div id="menu" [ngClass]="menuStateClass" style="position: absolute; top:0px; left: 0px;z-index: 800; height: 100%; color: #fff; background-color: #282d32">
<div style="margin-top: 35px; padding: 5px 0px 5px 0px;">
<ul class="menuList" style="overflow-x: hidden;">
<li>IsMenuVisible:{{menuvisible}}</li>
<li style="border-bottom: 1px solid #3d4247"><a (click)="toggleMenu()"><i class="fa fa-bars menuIcon" style="color: white; font-size: 16px;"></i></a></li>
<li *ngFor="#app of applications">
<a [routerLink]="[app.routerLink]">
<i class="menuIcon" [ngClass]="app.icon" [style.color]="app.iconColour" style="color: white;"></i>
<span [hidden]="menuStateTextHidden">{{ app.name }}</span>
</a>
</li>
</ul>
</div>
</div>
记得导入你需要的东西,例如
import {Component, EventEmitter, OnInit, Input, Output} from 'angular2/core';
强烈推荐 YouTube 上的这个视频: Angular 2 Tutorial (2016) - Inputs and Outputs
对于双向绑定,您需要类似的东西:
@Component({
selector: 'menu',
template: `
<button (click)="menuvisible = !menuvisible; menuvisibleChange.emit(menuvisible)">toggle</button>
<!-- or
<button (click)="toggleVisible()">toggle</button> -->
`,
// HTTP_PROVIDERS should now be imports: [HttpModule] in @NgModule()
providers: [/*HTTP_PROVIDERS*/, ApplicationService],
// This should now be added to declarations and imports in @NgModule()
// imports: [RouterModule, CommonModule, FormsModule]
directives: [/*ROUTER_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgForm*/]
})
export class MenuComponent implements OnInit {
@Input() menuvisible:boolean;
@Output() menuvisibleChange:EventEmitter<boolean> = new EventEmitter<boolean>();
// toggleVisible() {
// this.menuvisible = !this.menuvisible;
// this.menuvisibleChange.emit(this.menuvisible);
// }
}
并像
一样使用它@Component({
selector: 'some-component',
template: `
<menu [(menuvisible)]="menuVisibleInParent"></menu>
<div>visible: {{menuVisibleInParent}}</div>
`
directives: [MenuComponent]
})
class SomeComponent {
menuVisibleInParent: boolean;
}
我创建了一个简短的 plunkr。
ngModel Like Two-Way-Databinding for components
您至少有两种可能性来为组件创建双向数据绑定
V1:使用 ngModel Like Syntax,您必须在 @Output 属性 的末尾创建一个与 @Input 属性 + "Change" 同名的行输出属性 姓名
@Input() name : string;
@Output() nameChange = new EventEmitter<string>();
对于 V1,您现在可以使用 ngModel 语法绑定到子组件
[(name)]="firstname"
V2。只需创建一个 @Input 和 @Output 属性 并使用您喜欢的命名方式
@Input() age : string;
@Output() ageChanged = new EventEmitter<string>();
对于 V2,您必须创建两个属性才能获得双向数据绑定
[age]="alter" (ageChanged)="alter = $event"
父组件
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<p>V1 Parentvalue Name: "{{firstname}}"<br/><input [(ngModel)]="firstname" > <br/><br/>
V2 Parentvalue Age: "{{alter}}" <br/><input [(ngModel)]="alter"> <br/><br/>
<my-child [(name)]="firstname" [age]="alter" (ageChanged)="alter = $event"></my-child></p>`
})
export class AppComponent {
firstname = 'Angular';
alter = "18";
}
子组件
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'my-child',
template: `<p>V1 Childvalue Name: "{{name}}"<br/><input [(ngModel)]="name" (keyup)="onNameChanged()"> <br/><br/>
<p>V2 Childvalue Age: "{{age}}"<br/><input [(ngModel)]="age" (keyup)="onAgeChanged()"> <br/></p>`
})
export class ChildComponent {
@Input() name : string;
@Output() nameChange = new EventEmitter<string>();
@Input() age : string;
@Output() ageChanged = new EventEmitter<string>();
public onNameChanged() {
this.nameChange.emit(this.name);
}
public onAgeChanged() {
this.ageChanged.emit(this.age);
}
}