Angular2 - двухсторонняя привязка данных к свойству переменной компонента / класса компонента?

В Angular2 (Beta 6) у меня есть компонент для главного меню.

<mainmenu></mainmenu>

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

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

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

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

ИСКЛЮЧЕНИЕ: ошибки синтаксического анализа шаблона: недопустимое имя свойства 'menumodel.visible' ("

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

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

Ошибки синтаксического анализа шаблона: ошибка синтаксического анализатора: неожиданный токен '='

Однако это (односторонняя привязка?), Похоже, работает (но я мог бы захотеть, чтобы меню расширялось / сужалось от другого компонента, поэтому я чувствовал, что это должно быть свойство с двусторонней привязкой данных):

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

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

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

Это из шпаргалки по 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 (дочерний элемент root)

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} из angular2 / core;

Настоятельно рекомендую это видео на YouTube: Учебник по Angular 2 (2016 г.) - Входы и выходы


person DanAbdn    schedule 25.02.2016    source источник
comment
Я нахожу ваш вопрос довольно запутанным. Сообщения об ошибках IMHO не соответствуют предоставленному коду. Мне непонятно, чем вы на самом деле хотите заниматься. <mainmenu [(menuvisible)]="true"></mainmenu> не имеет особого смысла. Зачем вам нужна двусторонняя привязка к true? Это тоже не имеет смысла [(menumodel.visible)]="menumodel.visible". У вас не может быть свойства с ., и вы можете таким образом привязать его к подсвойству.   -  person Günter Zöchbauer    schedule 26.02.2016
comment
Я обновил вопрос отрывком из шпаргалки по Angular. Он показывает компонент с двусторонней привязкой данных. Итак, я предполагаю, что мне нужно будет сделать то, что я нашел в шпаргалке, вместе с вашим ответом ниже. например ‹Mainmenu [(menuvisible)] = true› ‹/mainmenu›?   -  person DanAbdn    schedule 26.02.2016
comment
menuvisible и vis - это отдельные свойства. Menumodel была переменной на основе классов, как и mainmenu. Код развивался по мере того, как я писал этот вопрос, извинения.   -  person DanAbdn    schedule 26.02.2016
comment
Меня это слишком сбивает с толку. ИМХО, вы можете удалить все, что вы пробовали, и попытаться объяснить, чего вы действительно хотите достичь. Добавьте минимальную реализацию компонентов, которые вы хотите использовать, и покажите, как они должны быть связаны (родительский, дочерний, ...).   -  person Günter Zöchbauer    schedule 26.02.2016
comment
Буквально вопрос заключается в том, как мне создать свойство двусторонней привязки для компонента, как написано в Angular Cheat Sheet ‹my-cmp [(title)] = name›   -  person DanAbdn    schedule 26.02.2016
comment
Я обновил свой ответ (не проверял).   -  person Günter Zöchbauer    schedule 26.02.2016
comment
Фантастика! Слишком много поздней ночи с Angular. Попробую завтра вечером и награду ответ соответственно. А пока большой огромный +1   -  person DanAbdn    schedule 26.02.2016
comment
Похоже, это было исправлено в Angular4 +, я использовал <mainmenu [(menuvisible)]="somedata"></mainmenu> без проблем   -  person Flavian Hautbois    schedule 01.06.2017


Ответы (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;
}
person Günter Zöchbauer    schedule 25.02.2016
comment
Должно работать практически без изменений. NgForm и NgClass больше не нужно указывать в directives. - person Günter Zöchbauer; 10.08.2016

Я создал короткий plunkr.

ngModel Like Two-Way-Databinding для компонентов

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

V1: с ngModel Like Syntax, вам нужно создать свойство @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 вам нужно создать два атрибута, чтобы получить двустороннюю привязку данных

[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);
     }
 }
person squadwuschel    schedule 15.05.2017