As many people done, we’ve changed our Angular 1 sites to a component model, so we’re already familiar with how components work. Angular 2 is ALL components, and while it is quite similar, it has some interesting structures and some irritating ones. Primarily, the ease at which there were output variables {binding : “=”} is just plain gone. I knew that was coming, but from a business aspect, sometimes you just need to “get’er done” if you want to get paid! Here’s a simple tutorial of how we replaced the “=” binding that so easily let us connect objects between parents and children in angular 1.
Angular 2 uses @Input() and @Output() to move data and functions from one component to another. These actually use Classes, similar to how you might move data in C# from one function to another using a model. The “convention” is to have each class in a file by itself named after the class. In one of our systems, we have a person’s address, so we made an “Address” class like this:
/* file name ../classes/address.ts */ export class Address { address1: string; address2: string; city: string; state: string; zipcode: string; }
Now, you can actually use it to import data from the outside world. It’s extremely easy. Instead of a “binding”, you simply put it in with your class-level variables. It’s a bit confusing at first, but here’s how it’s done and it makes sense after a while.
We start by telling the component that we’re actually going to do some importing & exporting of data by specfifically including that in the angular core import, just as you might have a “using” for system.data.sqlclient if you were doing some MS SQL work in your class.
import { Component, Input, Output, EventEmitter } from '@angular/core';
Now, just as you may have a “using” statement at the top of your c# controller/class, you have to import that class, just as you would do to import a custom model in your c# using statement. You do it like this:
/* note, our file name is ../components/address/address.component.ts, so we just use a relative path to the class. Later we might find a better way to do this, because I think it kinda sucks to hard-code this stuff. */ import { Address } from '../classes/address';
So, under that we have a component like this:
/* note: we use this dynamic querystring in the URL while we develop to avoid caching */ @Component({ selector: 'address', templateUrl: './app/address/address.component.html?v='+new Date().toUTCString() })
Then, beneath that, we have our address class like this:
export class AddressComponent { @Input() address: Address; constructor() {} }
now, to start with, let’s pretend that our @Input() address is just like : public address:any={“address1″:”123 some street”}. That object, however, is going to be coming from the “outside world”.
Got it so far? The @component sets the html element we use to call this component and tells us where it is (or you can put it right in there). Then we have a class that tells us that some or all of the data going into this element is coming from outside, which means, it’s going to be an attribute on the selector. So, we’d be writing it into another component’s html template like this:
Those brackets around address are pretty key. That means that the WhateverClassObjectNameYouDefinedAsAnAddress object is going to be sent into the address variable you defined in the @Import() of that component. In our case, it’s an object called “Address” (note: it converts attributes to lowercase)
So now, in your html template, you can use the object “address” (which is public by default) just as you would any variable in angular 1.
{{address.city}},{{address.state}} {{address.zipcode}}
That may seem like a lot, but it really isn’t all that big a deal when you come right down to it. You’re “using” an address model, you’re telling the class that it’s going to be getting that model full of data from a parent component, and then you’re using it.
Now that’s not to hard, right? Well, getting the data out is a pain in the ass. 🙂
kicking that data up to the parent component
This part is kind of a pain. I’d love it it did some kind of automatic push, but honestly, that is kind of inefficient and angular 2 is all about efficiency. So, how’s it done? With “emitters”.
In that example above, you might have noticed that I included the import of “EventEmitter”. That’s a tool that is used to populate an output variable and return it inside a function. (kinda weird if you’ve never done that before. javascript people love this)
Here’s that import, in case you don’t want to scroll up.
import { Component, Input, Output, EventEmitter } from '@angular/core';
We’ll just send back the same address object, except now include our new data from some input boxes. First, you need to define your Emitter, just like you defined the @Import address: Address, you do this:
@Output() addressUpdated: EventEmitter= new EventEmitter ();
Notice, I include an interface definition of the Address class, not the actual address. You don’t need to do this, but it makes it nice and clean. (You should already know about interfaces if you are using typescript)
So, WTF is is an event emitter? Honestly, I only use one portion of it right now. the “emit” feature. Basically, it will execute a function in the parent. You call it with a function inside this component.
So, suppose you have a template like this:
Ok, so that is going to fill out our address object with whatever is in those input boxes. Note the [(ngModel)]. That’s important. That’s like the ng-model=”address.address1″ in Angular1. However…. you HAVE to have the “name” attribute set too!! (that caught me for a bit when I was learning this!)
Now, you’ve got your little form set up, we’ve now got a button with a (click) attribute. This is the equivalent of ng-click=”update()”. So, we need a function in the component’s class that is named update(). The component class will look like this:
export class AddressComponent { @Input() address: Address; @Output() addressUpdated: EventEmitter= new EventEmitter (); constructor() {} update() { // later you will see what this is for this.addressUpdated.emit(this.address); } }
So, we now have a normal function that can do whatever you want and THEN, it calls that “emit” function of the EventEmitter, which we have called addressUpdated, and we’ve populated it with the address object that we filled out in our form. Weird for some, but not really too complex. However, it is actually kind of sending that “function” rather than sending the data. So in our parent, we’re going to need to decide what “function” will be receiving this data and then the parent can do whatever it wants with it.
Our html in the parent component will look like this now.