Angular2 two-way data binding - data-binding

I know Angular2 doesn't have two-way data binding but is there a way to mimick the two-way data binding behavior from Angular1.x?

Note - scroll down the answer for ng-model binding
You could actually do that, just that you need to invoke internal changelistener tick (similar to digest) to update binding in the zone, You can just add a (keyup) event for that. Similarly you could use directive bindings as well with properties dictionary of component settings.
Example:-
<input #label (keyup)>
<!-- variable #label represented as the element itself and accessible as property on controller instance
You can even bind keyup to a function or another another function and pass value from the label property-->
Display as:
<p>{{label.value}}</P>
Parent Component has a textbox and a label.
import { Component, bootstrap} from '#angular/core';
import {Display} from 'display';
#Component({
selector: 'my-app',
template: `<p><b>Parent Component:</b><p><input #label (keyup) (change)="handleChange(label.value)">
<p>{{label.value}}</P> <display [text]="label"></display></p></p>`,
directives: [Display]
})
class MainComponent {
label: any;
constructor() {
}
handleChange(label){
this.label = label;
console.log(this.label);
}
}
Now displaying it in child component as well:
#Component({
selector: 'edit',
template: `<p><b>Child Component:</b></p>{{text.value}}`
})
export class Edit {
#Input() text:any;
}
Demo
Update - ng-model for 2-way binding
Though Angular2 is one-time bound by default, ngModel sugar has been introduced to achieve 2-way binding. With that you could do for instance:
<input ngControl="name" [(ngModel)]="name">
Here usage of square brackets ([..]) suggests the property binding and round brackets ((..)) for event binding. Basically when you use ng-model, you are enabling both the bindings ngModel is more of an event. Behind the scenes it creates an observable event(with EventEmitter) to track the value changes in the bound element and update the bound property respectively.
For example:-
Include formDirectives:
import {FORM_DIRECTIVES} from '#angular/common';
and with form
<form (ngSubmit)="onSubmit()" let-f="form">
<input ngControl="name" [(ngModel)]="name">
<button>Click me and check console</button>
</form>
without form
<input [(ngModel)]="name">
<button (click)="onSubmit()">Click me and check console</button>
not necessary anymore
include formDirectives dependency in view annotation.
#Component({
template: .....,
directives: [FORM_DIRECTIVES]
})
Demo
Also read the nice write up from Victor Savkin on Two-way binding in angular2 by creating the ng-model event and how it works.

You can now simply do this by using ngModel using the following syntax:
<input [(ngModel)]="myProp" />
The combination of the square and round brackets means "two-way binding".
Please see the plunk here

Yes there is two-way binding in angular2. See here: https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngModel
So, how to use it in custom components?
What I like to do is something this:
private currentSelectedItem: MachineItem;
#Output() selectedItemChange: EventEmitter<MachineItem> = new EventEmitter<MachineItem>();
#Input() set selectedItem(machineItem: MachineItem) {
this.currentSelectedItem = machineItem;
this.selectedItemChange.emit(machineItem);
}
get selectedItem(): MachineItem {
return this.currentSelectedItem;
}
And use it like
<admin-item-list [(selectedItem)]="selectedItem"></admin-item-list>
You can also emit the new value where it is actually changed. But I find it quite convenient to do that gloabaly in a setter method and don't have to bother e.g. when I bind it directly to my view.

You can do this by attaching to the events on the input field and updating the internal value as is done in this example:
http://plnkr.co/edit/lOFzuWtUMq1hCnrm9tGA?p=preview
Create a component that has an internal attribute that holds the label this.label and a callback changeLabel that expects an event object
#Component({
selector: 'app',
templateUrl: 'bound.html'
})
class App {
label: string;
constructor() {
this.label = 'default label'
}
changeLabel(event) {
this.label = event.target.value;
}
}
bootstrap(App);
The create your template and attach the callback to the appropriate event (you could attach it to the keypress event but then you might need a timeout. I attached it to the change event for simplicity (which means you might need to tab off the input to see the update).
<label for="myinput">{{label}}</label>
<input id="myinput" type="text"/>
<p></p>You can change the label above by typing something below</p>
<label for="labeltext">New Label Text</label>
<input type="text" id="labeltext" (change)="changeLabel($event)"/>

There is another way to trick Angular2 into two-way binding. Don't pass a property but an object into the component. If you pass an object via one-way binding all of its properties are in fact two-way bound.
It makes the component less versatile as it needs to know the object but in many cases it's still useful.
I have a component that looks like this:
import { Component, Input } from "#angular/core";
import { NgSwitch, NgSwitchWhen, NgSwitchDefault } from "#angular/common";
export class Movie
{
public Title: string;
public Rating: number;
public Seen: boolean;
}
#Component
({
selector: "hh-image-checkbox",
template: `
<div [ngSwitch]="movie.Seen">
<div *ngSwitchWhen="true">
<img src="/Content/res/CheckTrue.png" (click)="onClick()">
</div>
<div *ngSwitchDefault>
<img src="/Content/res/CheckFalse.png" (click)="onClick()">
</div>
</div>
`,
directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]
})
export class ImageCheckboxComponent
{
#Input() movie: Movie;
public onClick()
{
this.movie.Seen = !this.movie.Seen;
}
}
It is invoked like this:
<hh-image-checkbox [movie]="movie"></hh-image-checkbox>
The movie object itself is one-way bound but all of its properties can be used for two-way binding.

Here is a simple plunker which demonstrates one way, two way and event driven approaches in action according to Angular2 2.0.0-beta.17
http://plnkr.co/eXZMoU
Two-way Event and property
<input [(ngModel)]="name" />
One way property
<input [value]="name" />
Event driven
<input (input)="name=$event.target.value">
We can dig Angular docs for more
[UPDATE 1/26/2020]
Since Angular2 beta libs are removed from project CDN ! above plnkr link doesn't work anymore.
Use below new plnkr Angular 6+ page , I ported previous page to NPMJS, new angular edition and new plnkr!
http://next.plnkr.co/edit/4okdOSgw3SMvdktR?preview

From the Docs:
Two-way binding ( [(...)] )
You often want to both display a data property and update that property when the user makes changes.
On the element side that takes a combination of setting a specific element property and listening for an element change event.
Angular offers a special two-way data binding syntax for this purpose, [(x)]. The [(x)] syntax combines the brackets of property binding, [x], with the parentheses of event binding, (x).
[( )] = BANANA IN A BOX
Visualize a banana in a box to remember that the parentheses go inside the brackets.
For more information, see
Angular Developer Guide - Template Syntax - Two-way binding
Angular #angular/forms API Reference - ngModel Directive

Its simple, try this;
<input [(ngModel)]="property" placeholder="property Value"/>
<h1>{{property}}</h1>

Related

How do you use element attributes with Svelte when compiling to a custom element?

I am trying to create a custom checkbox using Svelte and compile it to a custom element (with customElement: true) but I don't know what way is the proper way of using attributes.
Looking at the generated code it looks like the attributeChangedCallback and observedAttributes are generated automatically depending on what variables you export in the .svelte file so that seems to work as expected though I haven't found any documentation for this.
Right now I have this hacky approach of creating the checked property manually and managing the different values of the checked attribute (to match native behavior) as seen here:
<script>
import { onMount } from 'svelte';
export let checked;
onMount(() => {
if (checked != null) {
checked = true;
} else {
checked = false;
}
});
</script>
I then simply set the checked attribute to the input element and add a change event to keep the property updated when checking/unchecking the input:
<input
on:change={(event) => {
checked = event.currentTarget.checked;
}}
type="checkbox"
{checked}
/>
Is this how you are meant to use attributes and managing property states with Svelte and custom elements?
While that works, Svelte allows you to simplify it quite a bit:
<svelte:options tag="my-checkbox"/>
<script>
// `checked` will be `false` by default, but a user may pass in a
// different value through the attribute/property with the same name.
export let checked = false;
// this next line isn't strictly necessary. if you want to guarantee
// `checked` is always a boolean even if the user passes a different
// value, you can use a reactive statement to consistently convert
// `checked` into a boolean. this will re-run automatically.
$: checked = (checked !== false);
</script>
<!--
`bind:checked` is a shorthand for `bind:checked={checked}`, which in
turn means the `checked` property of the input will have a two-way
binding to the `checked` property of this component. updating one will
automatically update the other.
-->
<input type="checkbox" bind:checked>
Svelte's reactivity system will ensure the component's checked property/variable is kept in sync with the custom element's checked property, and as you noted, changes to the custom element's attribute will also trigger an update to the component.
Do note that you won't see the changes in the component reflected in the element's attributes, so you'll have to do that manually if necessary. Usually the property access is enough, though.

What is the difference between OnClick and #onclick in blazor components

I found out something curious and I am wondering if anyone knows the answer:
First of all this is not this question:
Different method calls in Blazor
That question refers to HTML elements. I am talking about Components.
So I have my own component named MyButton; and it has OnClick Parameter specified:
MyButton.razor
<button #onclick="OnClick">Do Something</button>
#code {
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
When I use MyButton I can use either the name exactly, i.e.
<MyButton OnClick="SomeMethod" />
But this is also working:
<MyButton #onclick="SomeMethod" />
When I remove the whole #code block from the MyButton.razor they both give me the exact same error message:
<Mybutton OnClick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'OnClick'.
<Mybutton #onclick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'onclick'.
The only difference is in the caps; "OnClick" vs "onclick"... that is logical. So it seems #onclick is the same as OnClick... but are they?
Is this simply an overload of some sorts?
#onclick is the native HTML click event and OnClick is the event parameter you explicitly expose in your MyButton component.
So in this case you should use
<MyButton OnClick="SomeMethod" />
I tried to reproduce the usage with #onclick but it didn't work in my case.
For science, you could try to add a text <p>Like this</p> to your MyButton component and see if the #onclick event still works and if it only fires if you click the button or also if you click the text.
Also, see Microsoft Docs for detailed information.
Do you use MudBlazor ? Because I think OnClick is part of the MudBlazor Button API and #onclick is part of the ASP.NET Core Blazor event handling features.

angular2 data binding between service and component properties

I need some clarification on binding between service and component properties and data binding in angular2
assume i have a service(singleton) and a component
export class Service {
name = "Luke";
object = {id:1};
getName(){return this.name};
getObject(){return this.object};
}
export class Component implements OnInit{
name:string;
object:any;
constructor(private _service:Service){}
ngOnInit():any{
//Is this 2 way binding?
this.name = this._service.name;
this.object = this._service.object;
//Is this copying?
this.name = this._service.getName();
this.object = this._service.getObject();
}
}
If you update elements by reference (if you update something into the object property), you will see the updates in the view:
export class Service {
(...)
updateObject() {
this.object.id = 2;
}
}
If you update elements by value (if you update something into the name property), you won't see the updates in the view:
export class Service {
(...)
updateName() {
this.name = 'Luke1';
}
}
See this plunkr: https://plnkr.co/edit/w7bS0fAVjOc3utnpD39b?p=preview.
Angular binding only works for bindings declared in the view (HTML).
If you want properties in your component being updated when values in a service change, you need to take care of it yourself.
Observables make this easy. See detect change of nested property for component input for an example.
If you want properties in a component updates as soon as a value in change in a service changes:
Import DoCheck from #angular/core and your service into the
component.
Call the service functions affecting the component property in ngDoCheck(){...}
The component view will be updated automatically as soon as any changes
Something like this in your component:
ngDoCheck() {
this.qty = this.cartService.getTotalQtyInCart();
}

Bidirectional data binding on a component input property

I am trying to make something work on angular2 and I am unable to find something about this behavior.
I have an application that implements a custom component like this one :
import {Component,Input} from 'angular2/core'
#Component({
selector:'my-comp',
template:`<input type="text" style="text-align:center; [(ngModel)]="inputText"> <p>{{inputText}}</p>`
})
export class MyComp{
#Input() inputText : string;
}
And I am trying to do a bidirectional databinding on my inputText variable from my component like this:
<my-comp [(inputText)]="testString"></my-comp>
Where the testString is a variable defined in the MyApp.ts which contains a string. I want my testString variable to be modified when my inputText is modified by the user.
Here is a Plunker with a simple sample code : https://plnkr.co/edit/zQiCQ3hxSSjCmhWJMJph?p=preview
Is there a way to make this works simply ? Do I have to implements an Angular2 class on my custom components and overload functions in order to make this works like an ngModel ? Do i necessarily have to create a inputTextChanged variable of EventEmitter type that emit my data when it's changed and do something like this :
<my-comp [inputText]="testString" (inputTextChanged)="testString = $event;"></my-comp>
Thank you in advance.
This is explained in the Template Syntax doc, in the Two-Way Binding with NgModel section:
<input [(ngModel)]="currentHero.firstName">
Internally, Angular maps the term, ngModel, to an ngModel input property and an ngModelChange output property. That’s a specific example of a more general pattern in which it matches [(x)] to an x input property for Property Binding and an xChange output property for Event Binding.
We can write our own two-way binding directive/component that follows this pattern if we're ever in the mood to do so.
Note also that [(x)] is just syntactic sugar for a property binding and an event binding:
[x]="someParentProperty" (xChange)="someParentProperty=$event"
In your case, you want
<my-comp [(inputText)]="testString"></my-comp>
so your component must have an inputText input property and an inputTextChange output property (which is an EventEmitter).
export class MyComp {
#Input() inputText: string;
#Output() inputTextChange: EventEmitter<string> = new EventEmitter();
}
To notify the parent of changes, whenever your component changes the value of inputText, emit an event:
inputTextChange.emit(newValue);
In your scenario, the MyComp component binds input property inputText using the [(x)] format to ngModel, so you used event binding (ngModelChange) to be notified of changes, and in that event handler you notified the parent component of the change.
In other scenarios where ngModel isn't used, the important thing is to emit() an event whenever the value of property inputText changes in the MyComp component.
I'll combine #pixelbits and #Günter Zöchbauer answers and comments to make a clear answer on my question if someone in the future is searching for this.
To make bidirectional data binding works on custom variables you need to creates your component based on the following.
MyComp.ts file :
import {Component,Input,Output,EventEmitter} from 'angular2/core'
#Component({
selector:'my-comp',
templateUrl:`<input type="text" style="text-align:center;"
[ngModel]="inputText" (ngModelChange)="inputText=$event;inputTextChange.emit($event);">`
})
export class MyComp{
#Input() inputText : string;
#Output() inputTextChange = new EventEmitter();
}
MyApp.ts file:
import {Component} from 'angular2/core'
import {MyComp} from './MyComp'
#Component({
selector:'my-app',
templateUrl:`<h1>Bidirectionnal Binding test </h1>
<my-comp [(inputText)]="testString"></my-comp><p>
<b>My Test String :</b> {{testString}}</p>`,
directives:[MyComp]
})
export class MyApp{
testString : string;
constructor(){
this.testString = "This is a test string";
}
}
There the bidirectional data binding to the inputText variable works correctly.
You can comment the answer for a more beautiful or simpler way to implement this code.
Your Plunker already contains the EventEmitter. The #Output() annotation is missing. To change the value call inputTextChanged.emit(newValue) (this also changes the value on inputText)
What I do is use a property, so when I change the data the change is emitted automatically
private _data: AnyType;
#Input() get data(): AnyType{
return this._data;
}
set data(value: AnyType) {
this._data = value;
this.dataChange.emit(this._data);
}
#Output() dataChange: EventEmitter<AnyType> = new EventEmitter();
In html you will bind the property using [(data)]
<comp [(data)]="getData()"></comp>

What is the 'angular' way of displaying a tooltip / lightbox?

I've been looking around and have not been quite able to get a clear path to the 'angular' way of accomplishing the following. What I'm trying to achieve is displaying a tooltip with information when hovering over a link within an ng-repeat loop. Based on my research, I understood that this is part of the view, and so I should probably handle this in a directive. So, I created an attribute directive called providertooltip. The html declaration is below:
<table>
<tr id="r1" ng-repeat="doc in providers">
<td>
<a providertooltip href="#{{doc.Id}}" ng-mouseover="mouseOverDoc(doc)" ng-mouseleave="mouseLeave()">{{doc.FirstName}} {{doc.LastName}}</a>
</td>
</tr>
</table
<div id="docViewer" style="display:hidden">
<span>{{currentDoc.FirstName}} {{currentDoc.LastName}}</span>
</div>
In the module, I declare my directive, and declare my mouseOver and mouseLeave functions in the directive scope. I also 'emit' an event since this anchor is a child scope of the controller scope for the page. On the controller function (docTable ) which is passed as a controller to a router, I listen for the event. Partial implementation is seen below:
app.directive("providertooltip", function() {
return {
restrict : 'A',
link: function link(scope, element, attrs) {
//hover handler
scope.mouseOverDoc = function(doc){
scope.currentDoc = doc;
scope.$emit('onCurrentDocChange');
element.attr('title',angular.element('#docViewer').html());
element.tooltipster('show');
//docViewer
};
scope.mouseLeave = function() {
element.tooltipster('hide');
}
}
}});
function docTable(docFactory, $scope, $filter, $routeParams) {
$scope.$on('onCurrentDocChange',function(event){
$scope.currentDoc = event.targetScope.currentDoc;
event.stopPropagation();
});
}
Ok, so here is my question. All of the works as expected; Actually, the tooltip doesn't really work so if someone knows a good tooltip library that easily displays div data, please let me know. But, what I'm really confused about is the binding. I have been able to get the tooltip above to work by setting the title ( default tooltip behavior ), but I can see that the binding has not yet occured the first time I hover of a link. I assume that the onCurrentDocChange is not synchronous, so the binding occurs after the tooltip is displayed. If I hover over another link, I see the previous info because as I mentioned the binding occurs in an asynchronous fashion, i.e., calling scope.$emit('onCurrentDocChange') doesn't mean the the parent scope binds by the time the next line is called which shows the tooltip. I have to imagine that this pattern has to occur often out there. One scope does something which should trigger binding on some other part of the page, not necessarily in the same scope. Can someone validate first that the way I'm sending the data from one scope to the other is a valid? Moreover, how do we wait until something is 'bound' before affecting the view. This would be easier if I let the controller mingle with the view, but that is not correct. So, I need the controller to bind data to the scope, then I need the view to 'display a tooltip' for an element with the data. Comments?
To go the angular way correctly start your directive like:
...
directive('showonhover',function() {
return {
link : function(scope, element, attrs) {
element.parent().bind('mouseenter', function() {
element.show();
});
element.parent().bind('mouseleave', function() {
element.hide();
});
}
...
Or start with http://angular-ui.github.io/ link to go the angular-way UI. Look into the bootstrap-ui module - pure angular bootstrap widgets implemented as directives. You can get a clue how the tooltip binding implemented directly from the source of the module - https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js
Also here is another example - (having jQuery and bootstrap scripts included) - use the ui-utils module Jquery passthrough directive ui-jq'. It allows to bind Jquery plugins ( style of $.fn ) directly as angular directive.
Here is their example for binding twitter bootstrap tooltip.
<a title="Easiest. Binding. Ever!" ui-jq="tooltip">
Hover over me for static Tooltip</a>
<a data-original-title="{{tooltip}}" ui-jq="tooltip">Fill the input for a dynamic Tooltip:</a>
<input type="text" ng-model="tooltip" placeholder="Tooltip Content">
<script>
myModule.value('uiJqConfig', {
// The Tooltip namespace
tooltip: {
// Tooltip options. This object will be used as the defaults
placement: 'right'
}
});
</script>
Also look into the official angular documentation for writing directives examples,
and have a happy coding time with Angular!

Resources