In this chapter we will be upgrading Socially from Angular1 to Angular2 using Angular's Upgrade Adapter. Why upgrading? Because Angular2 is the next major version of the framework and it will surely be the version we want to go with when building web apps in the future, and we can enjoy many of its benefits like better performance, server-side rendering, more powerful templating, better ecosystem and more. We will also be transforming our JavaScript code into TypeScript inspired by Angular2 team's recommendation.
First, we will remove the JavaScript compiler, whos belonging Meteor package is pbastowski:angular-babel
:
meteor remove pbastowski:angular-babel
Although it is possible to just replace the old JavaScript compiler with a TypeScript compiler, we would like to reserve ng-annotate
's compiler as well, since the updating process is gonna be done gradually and our app is gonna be a hybrid of Angular1 and Angular2. Thus, we're gonna install the following package:
meteor add mys:typescript-ng-annotate
Once our app is fully migrated to Angular2, we will replace mys:typescript-ng-annotate
with barbatus:typescript
, a package which will provide you with a pure TypeScript compiler. TypeScript compiler also relies on declerations, which can be installed via package manager called typings
. Further informations about typings
and how to install it can be found here. As for now just add the following configuration files:
1
2
3
4
node_modules/
typings/
.idea
npm-debug.log
1
2
3
4
declare module '*.html' {
const template: string;
export default template;
}
1
2
3
4
5
6
7
8
9
10
11
{
"name": "angular2-meteor-base",
"version": false,
"dependencies": {
"chai-spies": "registry:npm/chai-spies#0.7.1+20160614064916"
},
"globalDependencies": {
"meteor": "github:meteor-typings/meteor/1.3#955b89a4e2af892d1736bc570b490a97e860d5b7",
"node": "registry:env/node#6.0.0+20161019193037"
}
}
And run the following command which will install the declarations defined in the configuration files we've just added:
$ typings install
By now if will you run the app you will receive warning notifications by the TypeScript compiler. This is caused due to our app not being fully migrated to TypeScript. Once you finish the upgrading process and your app is purely based on Angular2 with proper declarations you shall receive no warnings.
Now that the compiler is ready we will start by switching our entry file into TypeScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import * as angular from 'angular';
import { Meteor } from 'meteor/meteor';
import { registerAuth } from '../imports/ui/components/auth/auth';
import { registerLogin } from '../imports/ui/components/login/login';
import { registerNavigation } from '../imports/ui/components/navigation/navigation';
import { registerPartiesList } from '../imports/ui/components/partiesList/partiesList';
import { registerPartiesMap } from '../imports/ui/components/partiesMap/partiesMap';
import { registerPartiesSort } from '../imports/ui/components/partiesSort/partiesSort';
import { registerPartyAdd } from '../imports/ui/components/partyAdd/partyAdd';
import { registerPartyAddButton } from '../imports/ui/components/partyAddButton/partyAddButton';
import { registerPartyCreator } from '../imports/ui/components/partyCreator/partyCreator';
import { registerPartyDetails } from '../imports/ui/components/partyDetails/partyDetails';
import { registerPartyImage } from '../imports/ui/components/partyImage/partyImage';
import { registerPartyMap } from '../imports/ui/components/partyMap/partyMap';
import { registerPartyRemove } from '../imports/ui/components/partyRemove/partyRemove';
import { registerPartyRsvp } from '../imports/ui/components/partyRsvp/partyRsvp';
import { registerPartyRsvpsList } from '../imports/ui/components/partyRsvpsList/partyRsvpsList';
import { registerPartyUninvited } from '../imports/ui/components/partyUninvited/partyUninvited';
import { registerPartyUpload } from '../imports/ui/components/partyUpload/partyUpload';
import { registerPassword } from '../imports/ui/components/password/password';
import { registerRegister } from '../imports/ui/components/register/register';
import { registerSocially, SociallyNg1Module } from '../imports/ui/components/socially/socially';
registerAuth();
registerLogin();
registerNavigation();
registerPartiesList();
registerPartiesMap();
registerPartiesSort();
registerPartyAdd();
registerPartyAddButton();
registerPartyCreator();
registerPartyDetails();
registerPartyImage();
registerPartyMap();
registerPartyRemove();
registerPartyRsvp();
registerPartyRsvpsList();
registerPartyUninvited();
registerPartyUpload();
registerPassword();
registerRegister();
registerSocially();
function onReady() {
angular.bootstrap(document, [
SociallyNg1Module.name
]);
}
if (Meteor.isCordova) {
angular.element(document).on('deviceready', onReady);
} else {
angular.element(document).ready(onReady);
}
Not only we changed the extension of the file, but we also made some adjustments to its content. All Angular1 modules registrations are now manually invoked, due to dependency on the upgrade who's gonna take part further in this tutorial. In addition, the way we import the angular
library have changed.
Before:
import angular from 'angular';
After:
import * as angular from 'angular';
Now we gonna go through a component transformation. We will start with the Socially component since it is the root component of our app. We will first change the way we import Angular-related libraries, just like demonstrated above:
1
2
3
4
5
6
7
8
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import * as ngMaterial from 'angular-material';
import * as ngSanitize from 'angular-sanitize';
import * as uiRouter from 'angular-ui-router';
import 'ionic-sdk/release/js/ionic';
import 'ionic-sdk/release/js/ionic-angular';
import 'ionic-sdk/release/css/ionic.css';
Second, we will specify the exported module as an Angular1 module, since we're dealing with two module systems which come both from Angular1 and Angular2, and we will implement a registration which is called from the entry file like we showed earlier:
11
12
13
14
15
16
17
18
19
20
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import webTemplate from './web.html';
import mobileTemplate from './mobile.html';
import { PartiesListNg1Module } from '../partiesList/partiesList';
import { PartyDetailsNg1Module } from '../partyDetails/partyDetails';
import { NavigationNg1Module } from '../navigation/navigation';
import { AuthNg1Module } from '../auth/auth';
class Socially {}
...some lines skipped...
const template = Meteor.isCordova ? mobileTemplate : webTemplate;
// create a module
export const SociallyNg1Module = angular.module(name, [
angularMeteor,
ngMaterial,
ngSanitize,
uiRouter,
PartiesListNg1Module.name,
PartyDetailsNg1Module.name,
NavigationNg1Module.name,
AuthNg1Module.name,
'accounts.ui',
'ionic'
]);
export function registerSocially() {
SociallyNg1Module
.component(name, {
template,
controllerAs: name,
controller: Socially
})
.config(config)
.run(run);
}
function config($locationProvider, $urlRouterProvider, $qProvider, $mdIconProvider) {
'ngInject';
Now we gonna simply switch all the file extensions from .js
to .ts
and repeat the recent process for each component. This process is a pain so if you don't wanna deal with it just git-checkout the next step of this tutorial.
Once your'e done we shall change the way we import the underscore
library otherwise our app might break:
1
2
3
4
import * as _ from 'underscore';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Email } from 'meteor/email';
Since we wanna use Angular2 we will have to install its essential packages:
6
7
8
9
10
11
12
13
14
15
16
17
18
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
"test:watch": "meteor test --driver-package sanjo:jasmine"
},
"dependencies": {
"@angular/common": "^2.1.0",
"@angular/compiler": "^2.1.0",
"@angular/core": "^2.1.0",
"@angular/forms": "^2.1.0",
"@angular/platform-browser": "^2.1.0",
"@angular/platform-browser-dynamic": "^2.1.0",
"@angular/upgrade": "^2.1.0",
"angular": "^1.5.3",
"angular-animate": "^1.5.3",
"angular-aria": "^1.5.3",
...some lines skipped...
"angular-material": "^1.0.7",
"angular-messages": "^1.5.3",
"angular-meteor": "^1.3.9",
"angular-sanitize": "^1.5.5",
"angular-simple-logger": "^0.1.7",
"angular-sortable-view": "0.0.15",
"angular-ui-router": "^0.2.18",
"angular-utils-pagination": "^0.11.1",
"angular2-meteor": "^0.7.0",
"angular2-meteor-polyfills": "^0.1.1",
"es6-shim": "^0.35.1",
"gm": "^1.22.0",
"ionic-sdk": "^1.2.4",
"meteor-node-stubs": "~0.2.0",
"ng-file-upload": "^12.0.4",
"ng-img-crop": "^0.2.0",
"reflect-metadata": "^0.1.8",
"rxjs": "^5.0.0-beta.12",
"underscore": "^1.8.3",
"zone.js": "^0.6.21"
},
"devDependencies": {
"angular-mocks": "^1.5.3"
Raw commands are listed below:
$ npm install --save @angular/common
$ npm install --save @angular/compiler
$ npm install --save @angular/core
$ npm install --save @angular/forms
$ npm install --save @angular/platform-browser
$ npm install --save @angular/platform-browser-dynamic
$ npm install --save @angular/upgrade
$ npm install --save angular2-meteor
$ npm install --save angular2-meteor-polyfills
$ npm install --save es6-shim
$ npm install --save reflect-metadata
$ npm install --save rxjs
$ npm install --save underscore
$ npm install --save zone.js
angular2-meteor-polyfills
will loadrxjs
,reflect-metadata
andzone.js
in their chronological order.
Now will start replacing our Angular1 components with Angular2 components, and it would be helpful if we could just do it gradually and not all at once, one component at a time. This is where the Upgrade Adapter kicks in. The adapter will give us the ability to downgrade Angular2 so they can be registered to Angular1 modules, and upgrade Angular1 components so they can be registered to Angular2 modules, thereby we can have a hybrid app. Let's get into business by initializing a new instance of the UpgradeAdapter
and passing it as an argument to each module registration:
1
2
3
4
5
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import * as angular from 'angular';
import { UpgradeAdapter } from '@angular/upgrade';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
import { registerRegister } from '../imports/ui/components/register/register';
import { registerSocially, SociallyNg1Module } from '../imports/ui/components/socially/socially';
const adapter = new UpgradeAdapter();
registerAuth(adapter);
registerLogin(adapter);
registerNavigation(adapter);
registerPartiesList(adapter);
registerPartiesMap(adapter);
registerPartiesSort(adapter);
registerPartyAdd(adapter);
registerPartyAddButton(adapter);
registerPartyCreator(adapter);
registerPartyDetails(adapter);
registerPartyImage(adapter);
registerPartyMap(adapter);
registerPartyRemove(adapter);
registerPartyRsvp(adapter);
registerPartyRsvpsList(adapter);
registerPartyUninvited(adapter);
registerPartyUpload(adapter);
registerPassword(adapter);
registerRegister(adapter);
registerSocially(adapter);
function onReady() {
angular.bootstrap(document, [
Note that you have to share the same instance of the adapter otherwise the application might not work as expected. Once we have our adapter we will create an Angular2 module which will represent our Angular2 app's module, and bootstrap our application using the adapter:
1
2
3
4
5
6
7
8
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
60
61
62
63
64
65
66
import 'angular2-meteor-polyfills/browser';
import * as angular from 'angular';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { UpgradeAdapter } from '@angular/upgrade';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
import { registerRegister } from '../imports/ui/components/register/register';
import { registerSocially, SociallyNg1Module } from '../imports/ui/components/socially/socially';
@NgModule({
imports: [
BrowserModule,
FormsModule
]
})
class AppNg2Module {}
const adapter = new UpgradeAdapter(AppNg2Module);
registerAuth(adapter);
registerLogin(adapter);
...some lines skipped...
registerSocially(adapter);
function onReady() {
adapter.bootstrap(document.body, [
SociallyNg1Module.name
]);
}
Angular1 filters have the same functionality as Angular2 pipes. Further details about the relation between filters pipes can be found in Angular's docs. Filters should be registered to Angular1 modules as normal and pipes should be registered to Angular2 modules, so even though they are almost identical they will have to be duplicated for now.
We will start with the displayName
pipe. This is how it should look like, and after we create a pipe it should be declared our app's Angular2 module:
27
28
29
30
31
32
33
34
35
36
37
38
import { registerPassword } from '../imports/ui/components/password/password';
import { registerRegister } from '../imports/ui/components/register/register';
import { registerSocially, SociallyNg1Module } from '../imports/ui/components/socially/socially';
import { DisplayNamePipe } from '../imports/ui/filters/displayNamePipe';
@NgModule({
declarations: [
DisplayNamePipe
],
imports: [
BrowserModule,
FormsModule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Pipe } from '@angular/core';
const name = 'displayName';
@Pipe({
name
})
export class DisplayNamePipe {
transform(user) {
if (!user) {
return '';
}
if (user.profile && user.profile.name) {
return user.profile.name;
}
if (user.emails) {
return user.emails[0].address;
}
return user;
}
}
The key concepts of this convertion are:
transform
method.The same process should be applied for uninvited
pipe as well:
28
29
30
31
32
33
34
35
36
37
38
39
import { registerRegister } from '../imports/ui/components/register/register';
import { registerSocially, SociallyNg1Module } from '../imports/ui/components/socially/socially';
import { DisplayNamePipe } from '../imports/ui/filters/displayNamePipe';
import { UninvitedPipe } from '../imports/ui/filters/uninvitedPipe';
@NgModule({
declarations: [
DisplayNamePipe,
UninvitedPipe
],
imports: [
BrowserModule,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Pipe } from '@angular/core';
import * as _ from 'underscore';
const name = 'uninvited';
@Pipe({
name
})
export class UninvitedPipe {
transform(users, party) {
if (!party) {
return false;
}
return users.filter((user) => {
// if not the owner and not invited
return user._id !== party.owner && !_.contains(party.invited, user._id);
});
}
}
We can't return false
inside an ngFor
directive so let's replace it with an empty array:
9
10
11
12
13
14
15
export class UninvitedPipe {
transform(users, party) {
if (!party) {
return [];
}
return users.filter((user) => {
We will focus on the PartyUninvited
to start with. Let's decorate it with a Component decorator, And like the pipe creation process we will also have to declare a component whenever we create it:
22
23
24
25
26
27
28
33
34
35
36
37
38
39
40
import { registerPartyRemove } from '../imports/ui/components/partyRemove/partyRemove';
import { registerPartyRsvp } from '../imports/ui/components/partyRsvp/partyRsvp';
import { registerPartyRsvpsList } from '../imports/ui/components/partyRsvpsList/partyRsvpsList';
import { registerPartyUninvited, PartyUninvited } from '../imports/ui/components/partyUninvited/partyUninvited';
import { registerPartyUpload } from '../imports/ui/components/partyUpload/partyUpload';
import { registerPassword } from '../imports/ui/components/password/password';
import { registerRegister } from '../imports/ui/components/register/register';
...some lines skipped...
@NgModule({
declarations: [
DisplayNamePipe,
UninvitedPipe,
PartyUninvited
],
imports: [
BrowserModule,
1
2
3
4
5
6
8
9
10
11
12
13
14
15
16
17
18
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import { Component } from '@angular/core';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
import UninvitedFilter from '../../filters/uninvitedFilter';
import DisplayNameFilter from '../../filters/displayNameFilter';
@Component({
template,
selector: 'party-uninvited'
})
export class PartyUninvited {
constructor($scope) {
'ngInject';
Since this is an Angular2 component it relies on pipes rather than filters, so we can remove the filters' importations:
5
6
7
8
9
10
40
41
42
43
44
45
46
import { Meteor } from 'meteor/meteor';
import template from './partyUninvited.html';
@Component({
template,
...some lines skipped...
// create a module
export const PartyUninvitedNg1Module = angular.module(name, [
angularMeteor
]);
export function registerPartyUninvited() {
Switching to angular2-meteor
would be an integral part of the migration. We will extend our component by MeteorComponent
so angular2-meteor's API would be available for use
1
2
3
4
5
6
7
11
12
13
14
15
16
17
18
19
20
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import { Component } from '@angular/core';
import { MeteorComponent } from 'angular2-meteor';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
template,
selector: 'party-uninvited'
})
export class PartyUninvited extends MeteorComponent {
constructor($scope) {
'ngInject';
super();
$scope.viewModel(this);
Once it's done we will replace angular-meteor's API with angular2-meteor's.
12
13
14
15
16
17
18
19
20
21
22
selector: 'party-uninvited'
})
export class PartyUninvited extends MeteorComponent {
constructor() {
super();
this.autorun(() => {
this.users = Meteor.users.find({}).fetch();
});
}
Now that we have our Angular2 component ready, we need to use it in our Angular1 module. The UpgradeAdapter's prototype contains a function called downgradeNg2Component
which will transform an Angular2 component into an Angular1 directive, once it is transformed we can declare it in our Angular1 module:
40
41
42
43
44
45
46
angularMeteor
]);
export function registerPartyUninvited(adapter) {
PartyUninvitedNg1Module
.directive(name, adapter.downgradeNg2Component(PartyUninvited))
}
Note that a component is a actually a directive so registering a directive achieves the same result.
The bindings of our Angular1 component can be translated to Angular2 using the Input decorator.
1
2
3
4
5
6
12
13
14
15
16
17
18
19
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import { Component, Input } from '@angular/core';
import { MeteorComponent } from 'angular2-meteor';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
selector: 'party-uninvited'
})
export class PartyUninvited extends MeteorComponent {
@Input() party: any;
constructor() {
super();
We will no longer use prefixed variables inside our template since Angular2's template engine doesn't support it:
3
4
5
6
7
8
9
10
11
12
</h4>
<md-list>
<md-list-item ng-repeat="user in users | uninvitedFilter:party" ng-click="invite(user)">
<p>{{ user | displayNameFilter }}</p>
</md-list-item>
<md-list-item ng-if="(users | uninvitedFilter:party).length <= 0">
Everyone are already invited.
</md-list-item>
</md-list>
Our template uses Pipes instead of Filters, so we will make the following adjustments:
3
4
5
6
7
8
9
10
11
12
</h4>
<md-list>
<md-list-item ng-repeat="user in users | uninvited:party" ng-click="invite(user)">
<p>{{ user | displayName }}</p>
</md-list-item>
<md-list-item ng-if="(users | uninvited:party).length <= 0">
Everyone are already invited.
</md-list-item>
</md-list>
The ngFor
directive of Angular2 is equivalent to Angular1's ng-for
:
3
4
5
6
7
8
9
</h4>
<md-list>
<md-list-item *ngFor="let user of users | uninvited:party" ng-click="invite(user)">
<p>{{ user | displayName }}</p>
</md-list-item>
<md-list-item ng-if="(users | uninvited:party).length <= 0">
And ng-if
directive is now ngIf
:
6
7
8
9
10
11
12
<md-list-item *ngFor="let user of users | uninvited:party" ng-click="invite(user)">
<p>{{ user | displayName }}</p>
</md-list-item>
<md-list-item *ngIf="(users | uninvited:party).length <= 0">
Everyone are already invited.
</md-list-item>
</md-list>
Events are registered using (parenthesis) rather than an ng
prefix:
3
4
5
6
7
8
9
</h4>
<md-list>
<md-list-item *ngFor="let user of users | uninvited:party" (click)="invite(user)">
<p>{{ user | displayName }}</p>
</md-list-item>
<md-list-item *ngIf="(users | uninvited:party).length <= 0">
Since PartyUninvited is a Angular2 component we have to change the way we're passing a value:
Bound attributes should be notated with [square brackets]:
24
25
26
27
28
<party-map flex="50" location="partyDetails.party.location"></party-map>
</div>
<party-uninvited flex [party]="partyDetails.party" ng-show="partyDetails.canInvite()"></party-uninvited>
</div>
Now let's do the same for PartyDetails
. First we will turn it into a component:
16
17
18
19
20
21
22
34
35
36
37
38
39
40
import { registerPartyAdd } from '../imports/ui/components/partyAdd/partyAdd';
import { registerPartyAddButton } from '../imports/ui/components/partyAddButton/partyAddButton';
import { registerPartyCreator } from '../imports/ui/components/partyCreator/partyCreator';
import { registerPartyDetails, PartyDetails } from '../imports/ui/components/partyDetails/partyDetails';
import { registerPartyImage } from '../imports/ui/components/partyImage/partyImage';
import { registerPartyMap } from '../imports/ui/components/partyMap/partyMap';
import { registerPartyRemove } from '../imports/ui/components/partyRemove/partyRemove';
...some lines skipped...
declarations: [
DisplayNamePipe,
UninvitedPipe,
PartyDetails,
PartyUninvited
],
imports: [
1
2
3
4
5
6
7
10
11
12
13
14
15
16
17
18
19
20
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import * as uiRouter from 'angular-ui-router';
import { Component } from '@angular/core';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
import { PartyUninvitedNg1Module } from '../partyUninvited/partyUninvited';
import { PartyMapNg1Module } from '../partyMap/partyMap';
@Component({
template,
selector: 'party-details'
})
export class PartyDetails {
constructor($stateParams, $scope, $reactive) {
'ngInject';
Since PartyDetails
component is dependent on an Angular1 component which outside our project's scope, we will have to upgrade it using the upgradeNg1Component
method:
46
47
48
49
50
51
52
53
const adapter = new UpgradeAdapter(AppNg2Module);
adapter.upgradeNg1Component('partyMap');
registerAuth(adapter);
registerLogin(adapter);
registerNavigation(adapter);
Then again, we will extend the MeteorComponent
:
2
3
4
5
6
7
8
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import * as angularMeteor from 'angular-meteor';
import * as uiRouter from 'angular-ui-router';
import { Component } from '@angular/core';
import { MeteorComponent } from 'angular2-meteor';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
template,
selector: 'party-details'
})
export class PartyDetails extends MeteorComponent {
partyId: string;
party: Object = {};
users: Object[];
isLoggedIn: boolean;
constructor() {
super();
this.subscribe('parties');
this.subscribe('users');
this.autorun(() => {
this.party = Parties.findOne({
_id: this.partyId
});
this.users = Meteor.users.find({}).fetch();
this.isOwner = this.party && this.party.owner === Meteor.userId();
}, true);
}
canInvite() {
And we will downgrade our newly created component so it can be loaded with Angular1's module system:
76
77
78
79
80
81
82
83
84
PartyMapNg1Module.name
]);
export function registerPartyDetails(adapter) {
PartyDetailsNg1Module
.directive(name, adapter.downgradeNg2Component(PartyDetails))
.config(config);
}
Our router might be implemented using Angular1's router, but the template can be Angular2's:
87
88
89
90
91
92
93
94
95
96
97
98
$stateProvider.state('partyDetails', {
url: '/parties/:partyId',
template: '<party-details [party-id]="partyDetailsRoute.partyId"></party-details>',
controllerAs: 'partyDetailsRoute',
controller: function($stateParams, $scope) {
'ngInject';
this.partyId = $stateParams.partyId;
},
resolve: {
currentUser($q) {
if (Meteor.userId() === null) {
Let's take care of the properties binding in the PartyDetails
component:
1
2
3
4
5
6
7
16
17
18
19
20
21
22
23
import * as angular from 'angular';
import * as angularMeteor from 'angular-meteor';
import * as uiRouter from 'angular-ui-router';
import { Component, Input, Output } from '@angular/core';
import { MeteorComponent } from 'angular2-meteor';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
selector: 'party-details'
})
export class PartyDetails extends MeteorComponent {
@Input() partyId: string;
@Output() party: Object = {};
users: Object[];
isLoggedIn: boolean;
Further information about the Output
decorator can be found here.
Once a component is fully integrated with Angular2, everything related to Angular1 can be removed, in this case, the PartyUninvited
component:
22
23
24
25
26
27
28
63
64
65
66
67
68
import { registerPartyRemove } from '../imports/ui/components/partyRemove/partyRemove';
import { registerPartyRsvp } from '../imports/ui/components/partyRsvp/partyRsvp';
import { registerPartyRsvpsList } from '../imports/ui/components/partyRsvpsList/partyRsvpsList';
import { PartyUninvited } from '../imports/ui/components/partyUninvited/partyUninvited';
import { registerPartyUpload } from '../imports/ui/components/partyUpload/partyUpload';
import { registerPassword } from '../imports/ui/components/password/password';
import { registerRegister } from '../imports/ui/components/register/register';
...some lines skipped...
registerPartyRemove(adapter);
registerPartyRsvp(adapter);
registerPartyRsvpsList(adapter);
registerPartyUpload(adapter);
registerPassword(adapter);
registerRegister(adapter);
8
9
10
11
12
13
71
72
73
74
75
76
import template from './partyDetails.html';
import { Parties } from '../../../api/parties';
import { PartyMapNg1Module } from '../partyMap/partyMap';
@Component({
...some lines skipped...
export const PartyDetailsNg1Module = angular.module(name, [
angularMeteor,
uiRouter,
PartyMapNg1Module.name
]);
1
2
3
32
33
34
import { Component, Input } from '@angular/core';
import { MeteorComponent } from 'angular2-meteor';
...some lines skipped...
);
}
}
Indeed, We can also move our app's design structure to Angular2 and reserve the API. Let's install the necessary packages
10
11
12
13
14
15
16
17
"@angular/compiler": "^2.1.0",
"@angular/core": "^2.1.0",
"@angular/forms": "^2.1.0",
"@angular/http": "^2.1.0",
"@angular/material": "^2.0.0-alpha.9-3",
"@angular/platform-browser": "^2.1.0",
"@angular/platform-browser-dynamic": "^2.1.0",
"@angular/upgrade": "^2.1.0",
Raw commands are listed below:
$ npm install --save @angular/http
$ npm install --save @angular/material
@angular/http
is just a peer dependency of@angular/material
We now have md-checkbox
, md-button
and md-input
available for us. But will first need to declare those:
4
5
6
7
8
9
10
40
41
42
43
44
45
46
47
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { UpgradeAdapter } from '@angular/upgrade';
import { MaterialModule } from '@angular/material';
import { Meteor } from 'meteor/meteor';
...some lines skipped...
],
imports: [
BrowserModule,
FormsModule,
MaterialModule
]
})
class AppNg2Module {}
Now that material2 is fully registered we can safely replace PartyDetails
template with Angular2's:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div *ngIf="party" layout="column" layout-padding>
<div layout="column" layout-gt-sm="row" layout-padding>
<form flex="50" layout="column">
<md-input [(ngModel)]="party.name" [disabled]="!isOwner" placeholder="Party name"></md-input>
<md-input [(ngModel)]="party.description" [disabled]="!isOwner" placeholder="Description"></md-input>
<div>
<md-checkbox [checked]="party.public">
Public Party?
</md-checkbox>
</div>
<div>
<md-button md-raised-button color="primary" (click)="save()">Save</md-button>
</div>
</form>
<party-map flex="50" [location]="party.location"></party-map>
</div>
<party-uninvited flex [party]="party" *ngIf="canInvite()"></party-uninvited>
</div>
The upgrading process for Socially might take a while and can be done in many different ways, but hopefully you got the concept. If your'e not familiar with Angular2-Metoer the following tutorial might come in handy.