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.
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.
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.
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.
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.
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.
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 on
s 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>
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.
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).
Just an example of using them together.
<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}}" />
<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.
<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.
<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 = '';
}