18

В Angular2 (Beta 6) У меня есть компонент для главного меню.Angular2 - двухсторонняя привязка данных по свойству компонента/компонента класса?

<mainmenu></mainmenu> 

Я хочу связать булевое значение для широкого или узкого. Так что я сделал это в этом:

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

Но то, что я хочу (я думаю), чтобы привязать к свойству яваскрипта класса (как я, возможно, есть и другие вещи, чтобы связываться, но хотите быть аккуратным при использовании одного класса в компонент).

Я получаю ошибку

EXCEPTION: Template parse errors: Invalid property name 'menumodel.visible' ("

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

Если я попробовать то же самое с одной переменной вместо класса я получаю:

Template parse errors: 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 View Model

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

Я пытаюсь создать меню, которое имеет значки и текст, но может пойти узкий, чтобы просто показывать значки. Я передам это событие вверх родительскому компоненту, чтобы изменить положение контейнера рядом с меню. Запуск контейнера содержимого для максимизации приведет к тому, что меню станет узким - я не говорю, что это лучший способ, но я хотел бы решить этот конкретный вопрос, прежде чем идти глубже.

Обратите внимание: я не привязываюсь к вводу данных здесь - просто привязывайте данные к компоненту, чтобы затем изменить пользовательский интерфейс.

Это от углового Cheatsheet

<my-cmp [(title)]="name"> 
Sets up two-way data binding. Equivalent to: <my-cmp [title]="name" (titleChange)="name=$event"> 

Заранее спасибо!

UPDATE

Интеграция кода из принятого ответа и адаптации для моего конкретного случая использования здесь окончательный рабочий код:

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.ц (ребенок корня)

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

Очень рекомендую это видео на You Tube: Angular 2 Tutorial (2016) - Inputs and Outputs

+0

Я нахожу, что вы сомневаетесь. Сообщения об ошибках IMHO не соответствуют предоставленному коду. Мне непонятно, что вы на самом деле хотите сделать. '' не имеет большого смысла. Зачем вам нужна двусторонняя привязка к «истине»? Это также не имеет смысла '[(menumodel.visible)] =" menumodel.visible "'. У вас не может быть свойство с '.', и вы можете привязываться к подзадачи таким образом. –

+0

Я обновил вопрос с помощью фрагмента из Angular Cheat Sheet. Он показывает компонент с двусторонней привязкой данных. Поэтому я предполагаю, что мне нужно будет делать то, что я нахожу в чит-листах вместе с вашим ответом ниже. например ? – DanAbdn

+0

menuvisible и vis - это одиночные свойства. menumodel была переменной класса, как и mainmenu. Код развивается, поскольку я написал этот вопрос извинения. – DanAbdn

ответ

27

Для двухсторонним связывании вам нужно что-то вроде:

@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; 
} 
+0

Большое спасибо - обновленный ответ с кодом - я понимаю теперь намного лучше. – DanAbdn

+0

Это все еще работает? Не удалось воспроизвести это в версии 2.0.0-rc4. – sdornan

+0

Должен работать почти без изменений. 'NgForm' и' NgClass' больше не нужно указывать в 'директивах'. –

4

I Я создал короткую сцену.

ngModel Like Two-Way-Databinding for components

У вас есть по крайней мере две возможностей для создания двухсторонних привязок данных для компонентов

V1: С ngModel синтаксисом, то вы должны создать свойство @output с той же именем линией @Input недвижимости + «Смена» в конце имени свойства @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 вы должны создать два атрибута, чтобы получить два способа Databinding

[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); 
    } 
}