Angular2 - 组件变量/组件类属性的两种方式数据绑定?

Posted

技术标签:

【中文标题】Angular2 - 组件变量/组件类属性的两种方式数据绑定?【英文标题】:Angular2 - two way databinding on a component variable / component class property? 【发布时间】:2016-06-08 22:57:03 【问题描述】:

在 Angular2(Beta 6)中,我有一个用于主菜单的组件。

<mainmenu></mainmenu>

我想为宽或窄绑定一个布尔值。所以我把它变成了这个:

<mainmenu [(menuvisible)]="true"></mainmenu>

但我想要(我认为)是绑定到 javascript 类属性(因为我可能有其他东西要绑定,但希望通过在组件中使用单个类来保持整洁)。

我收到一个错误

异常:模板解析错误:属性名称无效 'menumodel.visible' ("

][(menumodel.visible)]="menumodel.visible">

如果我尝试使用单个变量而不是我得到的类:

模板解析错误:Parser Error: Unexpected token '='

但是,这(单向绑定?)似乎确实有效(但我可能想触发菜单从另一个组件变宽/变窄,所以觉得这应该是双向数据绑定属性):

<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 视图模型类

export class MainMenuVM 
    public visible: boolean;
    constructor(
    )  this.visible = true; 

我正在尝试创建一个包含图标和文本的菜单,但可以缩小到仅显示图标。我会将此事件向上发送到父组件,以更改菜单旁边容器的位置。触发内容容器最大化将触发菜单变窄 - 我并不是说这是最好的方法,但我想在更深入之前解决这个特定问题。

请注意:我在这里不是对输入控件进行数据绑定 - 只是对组件进行数据绑定,这样我就可以修改 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(根的子级)

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>

记得导入你需要的东西,例如

导入 Component, EventEmitter, OnInit, Input, Output 从 'angular2/核心';

在 You Tube 上强烈推荐此视频: Angular 2 Tutorial (2016) - Inputs and Outputs

【问题讨论】:

我觉得你的问题很混乱。恕我直言,错误消息与提供的代码不符。我不清楚你到底想做什么。 &lt;mainmenu [(menuvisible)]="true"&gt;&lt;/mainmenu&gt; 没有多大意义。为什么要双向绑定到true?这也没有意义[(menumodel.visible)]="menumodel.visible"。您不能拥有带有. 的属性,您可以通过这种方式绑定到子属性。 我已经用 Angular Cheat Sheet 中的 sn-p 更新了这个问题。它显示了一个具有双向数据绑定的组件。所以我猜我需要做我在备忘单中找到的内容以及下面的答案。例如 ? menuvisible 和 vis 是单一属性。 menumodel 是一个基于类的变量,mainmenu 也是如此。当我写这个问题道歉时,代码一直在发展。 这对我来说太混乱了。恕我直言,您可以删除所有您尝试过的内容,并尝试解释您实际想要完成的内容。请添加您要使用的组件的最小实现,并显示它们应该如何关联(哪个是父级,哪个是子级,...)。 问题实际上是如何在组件上创建双向绑定属性,如 Angular Cheat Sheet 【参考方案1】:

我创建了一个简短的 plunkr。

ngModel Like Two-Way-Databinding for components

您至少有两种可能为组件创建双向数据绑定

V1:使用 ngModel 类似语法,您必须在 @Output 属性名称末尾创建一个具有相同名称的 @Output 属性 + @Output 属性+“更改”

@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);
     
 

【讨论】:

【参考方案2】:

对于双向绑定,您需要以下内容:

@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;

【讨论】:

应该几乎没有变化。 NgFormNgClass 不再需要在 directives 中列出。

以上是关于Angular2 - 组件变量/组件类属性的两种方式数据绑定?的主要内容,如果未能解决你的问题,请参考以下文章

React 自定义组件的两种方式

vue子组件修改父组件属性值的两种方法

Angular2,TypeScript,如何读取/绑定属性值到组件类(在 ngOnInit 中未定义)[重复]

在 Angular2 组件上使用 mixin 时缺少属性

组件和同一元素上的指令之间的两种方式数据绑定?

flutter组件