Component and Template Interaction in Ionic 2

Component and Template Interaction in Ionic 2

Follow Josh Morony on

Each page in an Ionic 2 application is a component, and every component has a class definition in a TypeScript file, and an HTML template.

The class and the template have a very important relationship and need to be able to communicate effectively. In this video tutorial, I introduce the concepts of Property Binding, Event Binding, Interpolation, and Two-Way Data Binding, all of which can help enable communication between the class and the template.

Here’s the video:

Video Notes

  • Property Binding allows you to bind dynamic data to DOM properties by adding square brackets to attributes like this: <img [src]="someVariable" />.
  • Event Binding allows you to bind DOM events on elements to function like this: <button (click)="someFunction()">
  • Interpolation allows you to render out expressions like variables and mathematical calculations into your template as a string
  • Two-Way Data Binding allows you to set up variables that will remain in sync no matter where they are changed. If the variable is updated in the class it will be reflected immediately in the template, if the variable is updated in the template it will be immediately reflected in the class.

Video Transcript

0:00

Today we are going to talk about property binding, event binding, and interpolation. These are basically more complicated ways of saying that we are going to discuss how the template in our components talks to the typescript file – the class.

0:24

I’m going to try and touch on the really basic concepts we have to work with and we are also going to work through a bit of an example as well. To start off with, I have already generated a blank application here which just has a simple avatar list that we are using. I just copied out the example from the Ionic Documentation. We are going to use this to take a look at some of the concepts.

0:45

Let’s jump into the code to see what we are working with.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
    <ion-list>
        <ion-avatar item-left>
            <img src="http://placehold.it/75">
        </ion-avatar>
        <h2>Something</h2>
        <p>Some other stuff</p>
    </ion-list>
</ion-content>

0:52

We just have the blank Ionic 2 application generated. We have the home page which is automatically generated. This is what is responsible for rendering out what we are seeing in the browser right now – a blank application with a list with one single list item. This is just one particular page in our application and we can have multiple different pages. We have the home page here. We could have a shop page, an about page, and each page that we have is a component. Components in Ionic 2, which is the same in Angular 2, all look like this:

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController) {

  }

}

1:26

They all have this typescript file with a component declaration which defines the selector, the template, and then they have the template itself and also a styling file which is a SASS file (Syntactically Awesome Style Sheets).

1:43

I’m not going to talk about styling the component in this video. I actually have a video recently where I talked through some methods of styling your application. So, I have a link to that here

1:55

We are just going to focus on the typescript file, the class definition, and the template file – the HTML file. Right now this HTML file isn’t really making use of this. This sets up the component for us so we can use it in the application but the template here is just doing its own thing, it is just using some basic Ionic 2 components.

2:18

To make this a little more interesting what we are going to do is make this list more dynamic. We are going to define some data in our typescript file that it is then going to use and loop over so we can display multiple items.

2:33

Let’s create that here in the home.ts file. We are going to create an array called employees.

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

    }

    someFunction(){

        let something = 'something';
        var something = 'something';
    }

}

2:46

We have created a member variable, or a class member, here employees: any. What this is, is a variable that belongs to this entire class. When we create a component using this class that is going to belong to the class as a whole rather than belonging to an individual function.

3:08

So, if I had a function in the home.ts file called someFunction and I define a variable in the normal way, let something = ‘something’; or we could do var something = ‘something’ which we don’t usually use anymore because with the new ES6 syntactic and Ionic 2 and Angular 2 ‘let’ is the better option generally because it uses block scoping so it limits the scope of the variable to wherever you define it so you don’t run into problems with variables clashing into each other.

3:50

The difference between the variable that we put below the constructor and the variable above is that the let something variable is only accessible from the one function whereas employees: any is accessible from any in this entire class and it is also available in our template file. We can reference that variable in the home.html which I am going to get to soon. I just wanted to make an important clarification here.

4:17

If we want to change the value of employees: any we could assign it anywhere within our class here. We can access it through ‘this’ which refers to the scope of the class. We can then do this below the someFunction:

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

    }

    someFunction(){

        let something = 'something';

        this.employees = 'whatever'

    }

}

4:31

We can access this.employees from any function in this entire class and then we can access this value in the template as well. We could even set the value in here using the template using two-way binding which again I am going to get to in just a little bit.

4:48

Let’s take a step back and delete the someFunctions(). Your typescript should now look like this:

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

    }

}

4:53

What we are going to do is create an array with a little bit of data in it to define a couple of employees.

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

        this.employees = [
            (picture:"http://placehold.it/75", name: 'Josh', title: 'Turtorial Guy'),
            (picture:"http://placehold.it/75", name: 'Bob', title: 'Some person'),
            (picture:"http://placehold.it/75", name: 'Jane', title: 'Some other person')
        ];

    }

}

5:10

We have defined an array on this this.employees now. Sorry for the uncreative data that I am using but I just have three people here, Josh, Bob, and Jane and they have a picture defined for their avatar, their name, and their title. We are going to make use of this data in the template.

5:29

Now, we just over to the home.html file where I can use an *ngFor directive. What I am going to do is looped over the <ion-item> using *ngFor.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
    <ion-list>
        <ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img src="http://placehold.it/75">
            </ion-avatar>
            <h2>Something</h2>
            <p>Some other stuff</p>
        </ion-item>
    </ion-list>
</ion-content>

5:42

The * syntax that you will see used for stuff like this is a structural directive that allows us to stamp out multiple different elements for a set of data. It is basically a shortcut for creating a template. What this will do is loop over this ion-item and it is going to create an ion-item for everything in the set of data that we are creating. It is going to stamp out the new information inside of that.

6:14

When this is actually output into the DOM what you actually see when you are looking at it through the browser it is not going to contain this *ngFor or any of the this we are writing here. It is going to generate the template using Angular.

Interpolation

6:45

Now I am going to change the <h2> to be the name. To do this I am going to using the concept of interpolation. With this, we can use any member variables we have defined in our class in the home.ts file. It is going to calculate whatever value that is and then turns it into a string and renders it out here.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list>
        <ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img src="http://placehold.it/75">
            </ion-avatar>
            <h2>{{employee.name}}</h2>
            <p>{{employee.title}}</p>
        </ion-item>
    </ion-list>
</ion-content>

7:10

This is going to now grab the name of an individual employee which is ‘Josh’ and it is going to print that out as a string

7:16

We can use any member variables that we have defined this the home.ts file. We can reference it directly in an interpolation with the curly brackets here {{}}. Then we are going to do the same with the title underneath in the <p> section.

7:52

Now we have a list. We have the three people that we set up in our array are now in the list.

8:09

We have our data being rendered out using interpolation. You can also use interpolation to do other calculations like simple mathematics like {{employee.name}} {{3 + 5}} which is going to display 8 in the list.

8:28

Most of the time you are going to be using this to render out some data but maybe you will run into a circumstance where you want to make use of that which essentially will run some code inside your template.

8:40

One thing I have skipped over on purpose is adding in the image. Because I am using the same link I am just going to delete the link in the <img> area. What you want to do is display the image in our data. Use interpolation again with the curly brackets and add in employee.picture.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list>
        <ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img src="{{employee.picture}}">
            </ion-avatar>
            <h2>{{employee.name}}</h2>
            <p>{{employee.title}}</p>
        </ion-item>
    </ion-list>
</ion-content>

Property Binding

9:27

The proper way to do this is the use property binding here which is what I mentioned at the start. It allows us to bind a variable we have to a property on the DOM. To do that we can use square brackets to the src [src]. This is what allows us to bind the DOM to the property and if we just write employee.picture without the curly brackets it will use that variable and add it to the source property.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list>
        <ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img [src]="employee.picture">
            </ion-avatar>
            <h2>{{employee.name}}</h2>
            <p>{{employee.title}}</p>
        </ion-item>
    </ion-list>
</ion-content>

10:08

This isn’t just used in the src, you can use this to bind to many different properties. The reason we use the square brackets here is because we will be turning this value into a string. It will first figure out what this string will be rather than using employee.picture directly. If I was to get rid of the square brackets around [src] it would break the image. We want to convert that variable which is in the home.ts. We want to grab that and turn it into a string and then use that rather than literally using the employee.picture.

11:09

You can use that in other circumstances as well. A common one to use is called hidden. So, if I go into the

<img [src]="employee.picture">

and say hidden=“true” to look like this

<img [src]="employee.picture" hidden="true">

11:25

You will see that the image no longer gets shown anymore. Instead of setting that value to true or false you might want to add a value I have in my typescript file. If I was to add another property to the home.ts file to say ‘show picture’ like this:

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

        this.employees = [
            (picture:"http://placehold.it/75", name: 'Josh', title: 'Turtorial Guy', showPicture: true),
            (picture:"http://placehold.it/75", name: 'Bob', title: 'Some person', showPicture: false),
            (picture:"http://placehold.it/75", name: 'Jane', title: 'Some other person', showPicture: true)
        ];

    }

}

12:02

Now we have different values for different people so if I now add the square brackets to the [hidden] in the home.html file to bind one of these properties to this attribute. If I change that to employee.showPicture.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list>
        <ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img [src]="employee.picture" [hidden]="employee.showPicture">
            </ion-avatar>
            <h2>{{employee.name}}</h2>
            <p>{{employee.title}}</p>
        </ion-item>
    </ion-list>
</ion-content>

12:23

Now the picture is showing with Bob and not the other two names.

12:34

A better name for the showPicture variable would be hidePicture so I am going to change that in the home.ts because we are deciding to hide it not show it.

Event Binding

12:55

So, that is Property Binding. We have already cover Interpretation. The main thing we have left to discuss is Event Binding.

13:05

Event binding allows us to bind events onto the events on the DOM and then create handlers for them in our typescript file. If I wanted to add a click event to this ion-item here to perform some action when that happens. I can use event binding to do that. Before we add a click handler we are going to turn the ion-item into a button.

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
    <ion-list>
        <button ion-item *ngFor="let employee of employees">
            <ion-avatar item-left>
                <img [src]="employee.picture" [hidden]="employee.hidePicture">
            </ion-avatar>
            <h2 tappable>{{employee.name}}</h2>
            <p>{{employee.title}}</p>
        </button>
    </ion-list>
</ion-content>

13:32

The reason we do this is that on mobile buttons and anchor tags don’t have the tap delay so by converting this to a button when getting rid of that tap delay. You can also add the tappable directive to get rid of that tap delay.

<h2 tappable>{{employee.name}}</h2>

13:51

But it is better for accessibility to use buttons and anchors tags when things need to be interacted with.

14:06

What I want to happen now is I want to call a function when that is tapped or clicked. I’m going to create that functions in the home.ts file.

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;

    constructor(public navCtrl: NavController) {

        this.employees = [
            (picture:"http://placehold.it/75", name: 'Josh', title: 'Turtorial Guy', hidePicture: true),
            (picture:"http://placehold.it/75", name: 'Bob', title: 'Some person', hidePicture: false),
            (picture:"http://placehold.it/75", name: 'Jane', title: 'Some other person', hidePicture: true)
        ];

    }

    handleClick(){
        console.log("I've been clicked!");
    }

}

14:25

To trigger that function in our template we can use an event binding. I will add the button in the home.html file two parentheses (). In this case in the parenthesis will be ‘click’ and then whatever the name of our function is, in this case, ‘handleClick’

<button (click)="handleClick()" ion-item *ngFor="let employee of employees">

14:50

Whenever this button is clicked now it is going to call in the handleClick function from the home.ts file.

14:56

If you switch over to your browser and try and click on the name and you will see that it renders out into the console that the name has been clicked.

15:06

As well as that, you can also pass in some data to that function. If I want to pass in the employee value so I know who has been clicked on I can just write ‘employee’ in the parenthesis ().

<button (click)="handleClick(employee)" ion-item *ngFor="let employee of employees">

15:18

The *ngFor directive is being used here to set up that reference to the employee I can pass that into the click function as well.

15:33

We what to make sure we are handling that in the home.ts file and logging out the employee object.

src/pages/home/home.ts

handleClick(employee){
    console.log("I've been clicked!");
    console.log(employee);
}

15:43

If I click on the name it still says it has been click and it outputs the object as well. This is useful for things like a master-detail pattern where you wanted to detect if somebody clicks on a specific item and you wanted to display more details for that item in a new page. This will delay to easily pass this object into a new page and display that data.

16:07

You can also blind to any DOM event that is available. This is a really simple syntax to handle listeners to events.

Two-Way Data Binding

16:19

The final concept I want to cover is two-way data binding. Two-way data binding allows you to set up a variable where if you change it in the template it is going to be changed in the typescript file automatically in the class. If you change it in the class it is going to be automatically updated to be reflected in the template.

16:41

To show you how two-way data binding works I am actually going to get rid of the HTML template completely to look like this:

src/pages/home/home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

</ion-content>

16:48

We are now going to grab an input from the Ionic Documentation. We are going to grab the Fixed Label input.

src/pages/home/home.html

<ion-content>
    <ion-list>

      <ion-item>
        <ion-label floating>Username</ion-label>
        <ion-input type="text"></ion-input>
      </ion-item>

    </ion-list>
</ion-content>

17:05

We will just use the username for now. If you jump over to your browser again you can see we have our username field in here and I can type in there and nothing else is happening right now.

17:18

To set up two-way binding we are going to use ngModel. First, we are going to create a variable in the home.ts file that we are going to use two-way binding on. We are now adding a ‘username’ to the class.

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;
    username: any;

    constructor(public navCtrl: NavController) {

        this.employees = [
            (picture:"http://placehold.it/75", name: 'Josh', title: 'Turtorial Guy', hidePicture: true),
            (picture:"http://placehold.it/75", name: 'Bob', title: 'Some person', hidePicture: false),
            (picture:"http://placehold.it/75", name: 'Jane', title: 'Some other person', hidePicture: true)
        ];

    }

    handleClick(){
        console.log("I've been clicked!");
        console.log(employee);
    }

}

17:35

And then we can set that up using ngModel, using the [{}] syntax and equal it to the member variable we just created in the home.ts, the username.

src/pages/home/home.html

<ion-content>
    <ion-list>

      <ion-item>
        <ion-label floating>Username</ion-label>
        <ion-input [{ngModel}]="username" type="text"></ion-input>
      </ion-item>

    </ion-list>
</ion-content>

17:50

I am setting the member variable in the home.ts file to say joshuamorony.

src/pages/home/home.ts

username: any = "joshuamorony";

17:58

Now, jumping back into the browser, you can see that it is now being displayed as joshuamorony.

18:14

This has allowed us to use the variable from the home.html file. The important thing to know is that not only is this grabbing the value from the typescript file and putting it into the home.html file, if we make a change to this in the template it is also going to be reflected against the member variable in the home.ts file.

18:35

If I create a button now for example and add another click handler and call the function submitUser. We are also going to add the ion-button styling.

src/pages/home/home.html

<ion-content>
    <ion-list>

      <ion-item>
        <ion-label floating>Username</ion-label>
        <ion-input [{ngModel}]="username" type="text"></ion-input>
      </ion-item>

    <button (click)="submitUser()" ion-buttons>Save</button>

    </ion-list>
</ion-content>

18:57

We are going to create a function in the home.ts file which is just going to log out some information for now.

src/pages/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    employees: any;
    username: any;

    constructor(public navCtrl: NavController) {

        this.employees = [
            (picture:"http://placehold.it/75", name: 'Josh', title: 'Turtorial Guy', hidePicture: true),
            (picture:"http://placehold.it/75", name: 'Bob', title: 'Some person', hidePicture: false),
            (picture:"http://placehold.it/75", name: 'Jane', title: 'Some other person', hidePicture: true)
        ];

    }

    handleClick(){
        console.log("I've been clicked!");
        console.log(employee);
    }

    submitUser(){
        console.log('save user');
    }
}

19:08

Now, jump into your browser and hit that ‘save’ button that is going to trigger that function.

19:16

What we are going to do now is take a look at what is happening to this username variable in the home.ts file. So, if we log the subitUser out as well.

src/pages/home/home.ts

submitUser(){
    console.log("save user");
    console.log(this.username);
}

19:35

If you click on the save button now we can see in the console that it logs out joshuamorony. If we change joshuamorony in the browser to just josh and save it again you can see that it has also updated the variable in the console and it is now displaying josh.

Another example of what you can do with this is to add another field;

src/pages/home/home.html

<ion-content>
    <ion-list>

      <ion-item>
        <ion-label floating>Username</ion-label>
        <ion-input [{ngModel}]="username" type="text"></ion-input>
      </ion-item>

    <button (click)="submitUser()" ion-buttons>Save</button>

    </ion-list>

    <h2>Employee Name: {{username}}</h2>

</ion-content>

20:06

Jump back into the browser again and as you edit the username ‘joshuamorony’ you can see the Employee Name is automatically being updated in that <h2> field.

20:18

This two-way data binding essentially allows you to keep variables and sync between the variable and the template and the class definition.

20:25

That is all we are covering in this video. I just wanted to cover the basics and will probably cover more advanced concepts in the future but hopefully, that clears up how these three basic but really import things work in Ionic 2 and Angular 2. That is event binding, data binding, and interpolation and also two-way data binding.

Check out my latest videos: