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
Angular v2+ supports two-way data binding using ngModel directive and also by having getter and setter methods.
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.
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.
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.
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; }
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); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With