Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 - two way databinding on a component variable / component class property?

Tags:

In Angular2 (Beta 6) I have a component for a main menu.

<mainmenu></mainmenu> 

I want to bind a boolean for wide or narrow. So I made it into this:

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

But what I want (I think) is to bind to a javascript class property (as I may have other things to bind but want to be tidy by using a single class in the component).

I get an error

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

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

If I try the same with a single variable instead of a class I get:

Template parse errors: Parser Error: Unexpected token '='

However this (one way binding?) does seem to work (but I might want to trigger the menu to go wide/narrow from another component so felt this should be a two-way data bound property):

<menu [vis]="true"></menu> 

This is a bit of my menu component:

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

Here is my MainMenu View Model class

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

I'm trying to create a menu which has icons and text, but can go narrow to just show icons. I will emit this event upwards to a parent component to alter the position of the container next to the menu. Triggering a content container to maximised will trigger the menu to go narrow - I am not saying this is the best way, but I would like to resolve this particular question before going deeper.

Please note: I am not databinding to an input control here - just databinding to a component so I can then modify the UI.

This is from the Angular cheatsheet

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

Thanks in advance!

UPDATE

Integrating the code from the accepted answer and adapting for my particular use case here the final working code:

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 (root)

export class AppComponent implements OnInit{    menuvisible: Boolean; } 

menu.component.ts (child of 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> 

Remember to import what you need e.g.

import {Component, EventEmitter, OnInit, Input, Output} from 'angular2/core';

Highly recommend this video on You Tube: Angular 2 Tutorial (2016) - Inputs and Outputs

like image 478
DanAbdn Avatar asked Feb 25 '16 22:02

DanAbdn


People also ask

Does angular2 support 2 way binding?

Angular v2+ supports two-way data binding using ngModel directive and also by having getter and setter methods.

What is used for two-way data binding in angular2?

Angular's two-way binding syntax is a combination of square brackets and parentheses, [()] . The [()] syntax combines the brackets of property binding, [] , with the parentheses of event binding, () , as follows.

How does two-way data binding work?

Two-way binding means that any data-related changes affecting the model are immediately propagated to the matching view(s), and that any changes made in the view(s) (say, by the user) are immediately reflected in the underlying model. When app data changes, so does the UI, and conversely.

Which of the following can be used to create two-way data bindings?

Two-way data binding can be achieved using a ngModel directive in Angular. The below syntax shows the data binding using (ngModel), which is basically the combination of both the square brackets of property binding and parentheses of the event binding.


2 Answers

For two-way binding you need something like:

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

And use it like

@Component({   selector: 'some-component',   template: ` <menu [(menuvisible)]="menuVisibleInParent"></menu> <div>visible: {{menuVisibleInParent}}</div> `   directives: [MenuComponent] }) class SomeComponent {   menuVisibleInParent: boolean; } 
like image 93
Günter Zöchbauer Avatar answered Sep 27 '22 22:09

Günter Zöchbauer


I've created a short plunkr.

ngModel Like Two-Way-Databinding for components

You have at least two possibilities to to create a two way databinding for components

V1: With ngModel Like Syntax, there you have to create a @Output property with the same name line the @Input property + "Change" at the end of the @Output property name

@Input() name : string; @Output() nameChange = new EventEmitter<string>();  

with V1 you can now bind to the Child Component with the ngModel Syntax

[(name)]="firstname" 

V2. Just create one @Input and @Output property with the naming you prefer

@Input() age : string; @Output() ageChanged = new EventEmitter<string>(); 

with V2 you have to create two attributes to get the two way databinding

[age]="alter" (ageChanged)="alter = $event" 

Parent Component

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

Child Component

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);      }  } 
like image 36
squadwuschel Avatar answered Sep 27 '22 22:09

squadwuschel