25. What is Databinding?

So far we've been dealing with static content. Now we will start to experiment a bit with dynamic data.

Time for Databinding :)

Databinding is essentially the communication between our HTML and our JS (or TS) code.

So in the case of React, we wouldn't be talking about databinding as it is already a part of JSX -> we can use JS directly inside of HTML, so we don't need any other type of 'special communication'. But for angular, we need databinding.

26. String Interpolation

In its lightest form, string interpolation means to use anything within the html that can be resolved into a string. It is done by putting whatever is resolved into string inside {{}} -> curly braces.

Example: component.ts:

...
export class ServerComponent implements OnInit {

  serverID = 10;  //a var declaration
  serverStatus =  "offline"; //another var declaration

  constructor() { }

  ngOnInit(): void {
  }

}

component.html

<p> {{ 'server'}} with ID {{ serverID }} is {{ serverStatus}}</p>

Note how we used the variables we declared inside the .ts file. These variables (one of which is a number) will all be resolved into strings when used inside the {{}} brackets. The number is allowed because it can be easily converted into a string.

You can also call a mehtod that returns a string:

.ts

export class ServerComponent implements OnInit {

  serverID = 10;
  serverStatus =  "offline";
  getServerStatus() {
    return this.serverStatus
  }
  constructor() { }

  ngOnInit(): void {
  }

}

.html

<p> {{ 'server'}} with ID {{ serverID }} is {{ getServerStatus}}</p>

Also: note we used a string directly into the first bracket. Since string will be resolved into string, we can do that (I guess even numbers?).

Note: you cannot use if else statement or any kind of conditional logic inside the {{}} -> only stuff that can be resolved into strings.

27. Property Binding

There are a lot of times where you can either use Proeprty Binding or String Interpolation (previous section).

Let's take this example:

server.componenet.html

<button class="btn btn-primary" disabled>Add Server</button>

Now we want to let the button to be abled or disabled based on a certain criteria. In real life apps, this criteria would usually be something complex. To keep it simple in this example, we will create a simple 2sec timer function after which the button should become active:

server.component.ts

export class ServerComponent implements OnInit {

  allowNewServer = false;
  
  constructor() { 
    setTimeout(() => {
      this.allowNewServer = true;
    }, 2000);
  }

  ngOnInit(): void {
  }

}

Note that initially, the allowNewServer variable is set to false. The constructor is similar to componentDidMount in React: it will be executed once at the beginning of the lifetime of the component.

This is not enough, we now have to bind the property in the html:

server.componenet.html

<button class="btn btn-primary" [disabled] = "!allowNewServer">Add Server</button>

Note that here we made a binding to an existing HTML property (read more on property vs attribute). We did this by putting square brackets around the property disabled, and then giving it the value of the variable we want to bind.

This is it.

You don't always bind properties inside html, there are other ways of binding properties.

28. Property Binding vs String Interpolation

Note: just like a number, a boolean is also easily cast into a string.

Note2: Both property binding and string interpolation are used to output data into the UI. However, they cannot, so far, react to that data.

Lets take example of something that we can do in both property binding and string interpolation:

Assume we have the same 'allowNewServer' method from the previous section (this method returns a boolean). And assume we want to print its result into the screen.

String Interpolation:

<p> {{allowNewServer}}</p>

Property Binding:

<p [innerText]="allowNewServer"></p>

These two will output the same result into the screen. In general, when we just want to output string, use String Interpolation. When you want to output something based on a certain variable (like the button example earlier), use Property Binding.

29. Event Binding

Now we will 'react' to events, like a button click.

This is mostly done through methods. It is good practice to start the method name with on in order to know that the method will be called from within the html template (because mainly that's where we will react to stuff).

Following the previous example, here is the server.component.ts (just focus on the serverCreationStatus variable and the onCreateServer() method:

export class ServerComponent implements OnInit {

  allowNewServer = false;
  serverCreationStatus = 'No server was created!';

  constructor() { 
    setTimeout(() => {
      this.allowNewServer = true;
    }, 2000);
  }

  ngOnInit(): void {
  }

  onCreateServer(){
    this.serverCreationStatus = 'Server was created';
  }

}

We want to listen to the click event on a button we previously created. In normal html, we would have used the onclick event listener. However, in Angular, we have the angular event binders that we should use.

Event binders use the normal parenthesis () (just like the property binding used the [] thingy).

Here we want to use the (click) event binder. Note that usually, the names of the angular events are similar to the html ones, except that they dont start with on (onclick becomes click, onmouseenter becomes mouseenter and so on).

server.component.html

<button class="btn btn-primary" 
        [disabled] = "!allowNewServer" 
        (click)="onCreateServer()">Add Server
</button>
//this will output a different result based on the click
<p> {{serverCreationStatus}}</p>

Notes: we can put any code that we want to be executed between the "" of (click). It is customary to call a method though. Make sure to always put the parentheses when you are calling the method.

30. Bindable Properties and Events

How do you know to which Properties ([disabled], etc) or Events ((click), etc) are available to use for binding? You can basically bind to all Properties and Events - a good idea is to console.log() the element you're interested in to see which properties and events it offers.

Important - Repeating a note: For events, you don't bind to onclick but only to click (=> (click)) [remove the 'on's].

The MDN (Mozilla Developer Network) offers nice lists of all properties and events of the element you're interested in. Googling for YOUR_ELEMENT properties or YOUR_ELEMENT events should yield nice results.

31. Passing and Using Data with Event Binding

Now cosider the following code:

server.component.html

<label>Server Name</label>
<input 
    type="text"
    class="form-control"
    (input)="onUpdateServerName($event)" />

Note how we created a method and binded it to input - which means the the method will fire whenever the input has changed (this is an exisiting event we're binding to - originally it would have been oninput but no ons here).

Also, this is a type of event that expects to send data somewhere else (in this case, it is the string that the user is inputting). So we use the $event keyword to capture that data.

$event is a reserverd word and can't be replaced with something else.

server.component.ts

 onUpdateServerName(event: any){
    console.log(event);
  }

//this code is inside the component class

You will notice that the information we're usually interested in is inside the target -> value part of console-logged object above.

That's why we can rewrite the above to be :

component.ts (inside component class code):

  serverName = '';
  
  onUpdateServerName(event: any){
    this.serverName = event.target.value;
  }

Note: sometimes typescript might give an error regarding the specification of the element that you are taking the event from. Review this part in the course if needed.

component.html


<label>Server Name</label>
<input 
    type="text"
    class="form-control"
    (input)="onUpdateServerName($event)">
<p>{{serverName}}</p>

32. Important: FormsModule is Required for Two-Way-Binding!

Important: For Two-Way-Binding (covered in the next lecture) to work, you need to enable the ngModel directive. This is done by adding the FormsModule to the imports[] array in the AppModule.

You then also need to add the import from @angular/forms in the app.module.ts file:

import { FormsModule } from '@angular/forms';

This is something you will do for every project.

33. Two-Way-Databinding

This is where we combine both: event binding and property binding :)

So instead of the previous code (commented below), we have a new code:

<!-- <input 
    type="text"
    class="form-control"
    (input)="onUpdateServerName($event)"> -->
<input 
    type="text"
    class="form-control"
    [(ngModel)] = "serverName">

Note how we have both [] and (). Because it is combining both event and property binding. Wow.

Remember to have included the forms thing (as mentioned in previous sections) to be able to use ngModel

Now what will this [(ngModel)] = "serverName" do?

It will be trigered on the input event*, and at the same time, will update the value of serverName in our component automatically.

On the other hand, if I updated the value of serverName through some other function or whatever, the value inside the input will also change accordingly.

(= two way binding)

Note that we are using ngModel here. ngModel is one of many directives available for us to use in angular. This particular directive binds to an input, seclect, textarea, or custome form control. Google it for further reading.

So we can for example have an initial value for serverName -> and it will automatically get placed inside the input, then change accordingly as we update it.

Nicy nicy. (In React, this needs more setup to be done. Still team React though).

34. Combining all Forms of Databinding

Just an example of using them together.


Review:

  1. String interpolation:
<div>{{phoneNumber}}</div>
    <!--phoneNumber should be declared in the .ts file -->
    

Basically it can take any variable or value as long as that variable or value is a string or can be naturally turned into a string.

It is used for anything that needs to be turned into a string. We can, for example, use it inside an img src tag or alt tag as such:

<img src="{{recipe.imagePath}}" alt="{{recipe.name}}" />
    
  1. Property Binding
<button [disabled] = "!allowNewServer">Add Server</button>

<!-- allowNewServer is defined in the ts file -->

Html elements naturally have properties (ex: disabled, innerText, etc). We can bind variables to these properties in angular, as seen above (put the property inside [] square brackets,and pass the variable as though you are passing a string).

Property binding can help with properties that will change in value based on a variable and so on. [Maybe we can write whatever javascript we want inside the "" -? I am not sure]

They can also behave just like String Interpolation if you binded to the [innerHtml] property, for example.

  1. Event Binding
<button (click)="onCreateServer()">Add Server
</button>

<!-- The onCreateServer() method is declared in the ts file--> 

Just like there are natural properties for html elements (which we can use in property binding), there are also natural events for each html element, such as (click), (input), and so on.

We usually bind methods to these events. These events usually start with on in the normal html - but in angular event binding, we remove the on and put the event inside () round brackets when we want to bind to it. Just like property binding, whatever we bing we put inside a string.

  1. 2-way data binding
<input [(ngModel)] = "serverName" />
<!-- serverName is declared inside the .ts file-->

When using 2-way databinding, we should use special directives (in this case, we're using ngModel). These directives are there to stand in place of an event + a property at the same time.

If the value of serverName was changed by some other function, the value of the element input will also change. If the user inputs something into the input field, the value of serverName will change too. That's why it's called 2-way databinding.

Note from assignment: if your bindings are simple enough, you can put them right there inside your html. In the following example, we inlcuded something that resolves into true of false for the [disabled] binding, and we included an instruction for the (click) binding instead of creating a method for it:

<input  [(ngModel)] = "username"/>
<p>{{username}}</p>
<button [disabled] = 'username === ""' (click)='username = ""'>Click Me</button>

Where this is the component.ts:

export class AppComponent {

  username : string = '';
}