From Donkey to Unicorn, A new approach to AngularJS migration – Asim Hussain – AngularConnect 2017

, , Leave a comment


ASIM: Hello. Can you hear me? Awesome. So I talk about Angular migration a lot. I spoke about it at ngConf this year, at Barcelona,
at AngularCamp, in Israel, at AngularUp, I’ve run workshops on it, and I’ve spoken on it
at Meetups as well. But the same thing happens at my lectures. I get a bunch of people — I’m sure a few
of you will come to me afterwards, a little bit frustrated at the ng upgrade approach. It isn’t working for your use case. It’s a great solution. A technical marvel, I think. No other framework has put this much effort
into helping people migrate from one version to the next. But it doesn’t really cover all the use cases. So I’m gonna show you an alternative approach
today. An alternative to the ng-upgrade module approach. It has its own downsides, but I believe it
covers a broader range of use cases. And I actually call it bullet proof migration. Well, today I’m calling it bullet proof migration. The name changes on a weekly basis. What are we gonna talk about? First off I’m gonna do a really quick overview
on the ng-upgrade approach. Really quick, to make sure everybody is on
the same page. Then I’m gonna talk about why I think most
migrations fail or I think many migrations fail. And then I’m gonna explain the solution I’m
talking about. So thank you very much for the intro, Tracy. But… Here’s another one. My name is Asim Hussain. You can find me on Twitter at @jawache. Everyone gets confused. That one is jaw-ache, not jowashy. And I blog at codecraft.tv, and I’m a cloud
developer advocate at Microsoft. Which means I work with the Azure team. If any of you use it or don’t use it and have
any questions on it, come speak to me afterwards. So really quick intro to the ng-upgrade. Just for the colorblind of you in the audience,
when I say Angular, it’s gonna be blue. When I say AngularJS, it’s gonna be red. We will dual boot the application with both
AngularJS and Angular 5 libraries. By dual booting, this gives us the ability
to basically upgrade one entity at a time. Be that a service, a resource, a controller,
whatever. And then what we do, the way I recommend migrating
using ng-upgrade, is going through the leaves of your application, upgrading one entity
at a time, until eventually everything is Angular 5 and you drop AngularJS and you have
an Angular 5 application. Okay, great. That’s great. So then why do migrations fail? And this is my opinion. I think it’s for two reasons. One I call other baggage. And the other one I call clean house. And I’m gonna explain these two terms in a
second. Let’s start off with other baggage. So we don’t build our applications in isolation. Okay? We use a lot of other modules. So when you’re building an AngularJS application,
for instance, you might be using Bootstrap library, Bootstrap 2.3.2. When I was building AngularJS on a daily basis,
I would be using ng-bootstrap — can’t remember. You use a whole bunch of other third party
libraries. Okay? So when we migrate, some of these modules
are then absorbed into the main Angular framework. For instance, I would drop ui-routes and use
the angular-router. Might not use angular-strap. So sometimes you want to upgrade some of these
modules. I might want to move from bootstrap 2 to bootstrap
4. Makes sense. But we’re not running two separate applications
when we’re migrating, using ng-upgrade module. We’re running one application. Using one global namespace. So that means it has to share the same modules
and the same baggage. For instance, it means you can’t have two
versions of Bootstrap running. Okay? You’re stuck with one. So what are some of the solutions? Well, one of them is: You just keep the baggage. So then as you migrate your AngularJS application
to Angular 5, you then still keep Bootstrap 2. So now you have an Angular 5 application running
with… Bootstrap 2. Which never really felt good to me. I’ve worked with clients where we’ve had to
go by this approach. And it actually has an additional problem,
where people might be building Bootstrap 3 or 4, Angular 5 components and releasing them. Nobody is creating a Bootstrap 2 date picker
with Angular 5 support. At all. So that’s one solution. It’s not a great solution. What’s another solution? The other solution I found was just to migrate
your AngularJS application to Bootstrap 4. And then migrate to 5. Okay? That never felt good either. You’re putting all this effort to migrate
this legacy application that you’re trying to get rid of in the first place, to Bootstrap
4, only to just throw away that code later on. Ideally we want a solution where your AngularJS
application is using 2, and as you migrate to 5, at the same time, you migrate to 4. That’s the goal. That’s what we want. Right? So what’s the other reason I think migrations
fail? I call it clean house. I say you have to clean your house before
you migrate. Now, when AngularJS first came out, we had
no idea how to architect a good AngularJS application. We had no idea. We would use controllers instead of components. I use scope inheritance as a core feature
of my architecture. But now we know we’re supposed to use controller-as. I used scope watch probably far more than
I should have. And maybe used emit and broadcast too much. So in terms of the current standards, my old
AngularJS applications probably sit around here. But imagine if you were to build an AngularJS
application today, with everything we now know about how to build a good AngularJS application. That’s where you need to be, before you start
your migration journey. Okay? You can kind of upgrade as you go along, and
fix things. But in my experience, in working with a bunch
of clients on this, it’s far better just to take your AngularJS application, bring it
to today’s standards, Typescript, file structure, whatever, before you start your process of
migration. But like most of us, we’re staring at complex
architectures. Yeah? With years of investment. I mean, Angular has been out for a year. So if you all are still working on AngularJS
application right now, it’s probably two, three, four years old. Maybe five. Right? It’s had years of investment. Maybe tens of thousands of hours of investment. I’m telling you… By the way, you need to refactor everything
to meet modern standards. It’s not really possible, right? It’s at best a very depressing request and
at worst, completely impossible to do. And for a lot of people who I explained this
to, they said… Well, it’s almost the same as just rewriting
it from scratch. So that’s why I’ve come up with another solution. I would call it a halfway between a rewrite
from scratch and a migration approach. And I would have to say… I didn’t come up with this solution myself. It was actually at my last engagement, before
I was working at Microsoft. My replacement was coming along and I was
training him up. I spent six months getting this ugly framework
over, to get working onto Angular. And after a few minutes, he turned to me and
said… Well, why don’t you just use an iFrame? I was like… What? He was like… Yeah, just have a modern Angular SPA, built
using the modern framework, and then if you want to show the old content, just… IFrame in your old AngularJS application. And I was like… I was like… My first reaction was to really object. I remember… I actually remember being next to him and
I was like no! But then I thought about it for a few minutes. And I realized… Actually… That’s not a bad idea. I actually started feeling dumb for not thinking
of it in the first place. So I worked on it. Fairly recently. A couple of months ago now, I think. And basically came up with a demo. I’ll show you the demo right now. It’s pretty simple. You have to trust me. This is currently a Bootstrap 4, Angular 4
application. Normal URL, hash-based routine, to slash Angular
at the end. And then what do I do now? Can’t remember. Let me click on AngularJS. And those with sharp eyes can instantly see
this is a Bootstrap 2 application with Angular 1.6. The key thing is I think I go… And then it’s the same URL. With just AngularJS at the end. And then if I inspect element, scroll up,
it’s just an iFrame. Okay? And you see… It’s a router outlet and I’ve got a component
called app iFrame that I’m injecting in. If I go back to the Angular application, inspect
element again, it’s just a standard Angular application, with something called an app
counter component I’m injecting in. And the other thing is… I’m clicking — you have to click the number. It increases. But if you see, the number is shared. The state is shared between both of these
applications. The two iFrames. The two completely separate applications. And we’re sharing routing and we’re sharing
state. Okay? So that’s what I’m talking about. They’re completely separate applications. They have their own global namespace. So that means you don’t have to worry about
the baggage. Your Angular 5 application can use whatever
modules and third party libraries that it wants. It’s using Bootstrap 4. Your AngularJS — it has a clean house, so
your AngularJS application can still stay. Its wonderful, unique architecture can still
stay. You don’t need to touch it. So what are some of the core concepts you
need to figure out to get this working? One of them is called route ownership. And the other one is just basically shared
state. Okay? So I’m gonna go through a bunch of these. So for route ownership, we start with an Angular
application that just iFrames in our AngularJS application. Or just imagine — the whole thing is just
an iFramed AngularJS application. So all the URLs are handled by your AngularJS
application. It might be a little too subtle. So basically then Angular starts taking over
the routes. You write the route in Angular, and then configure
the application so every time that route is requested, we show the Angular app, and not
the AngularJS application. And then that’s how we migrate. We migrate one route at a time. Over from AngularJS to Angular. So eventually it’s just Angular. Okay? So what do I mean by Angular starts taking
over? So in this slide, you’ll see that the top
is AngularJS with — it’s actually UI router configuration. And the bottom is just Angular router configuration. So what I mean by this is… We might start off with our Angular application
as just one route. Before that route at the end. All of our routes are in an AngularJS application. And then we simply just… Once you migrate a route, we just simply delete
it from our Angular route of configuration and move it to our Angular app route Angular
router. But then we get into a thorny issue. For a period of time, you’re gonna have both
an AngularJS and an Angular app both handling different routes first thing in an application. So who owns the route? Angular or AngularJS? And how do you coordinate between the two? I’m gonna explain this with a bunch of use
cases. So this is one use case. So somebody is on page 5, which is AngularJS. They click something and the thing they want
to click to is page 4. Which is, again, just handled by AngularJS. There’s nothing you need to do here. It’s all handled within the same application. Again, we’ve got Angular. Someone clicks on page 2, which is Angular. They want to go to page 1, it’s just Angular. So it’s all handled within the same application. So there’s nothing you need to do. The interesting thing is something like this. So somebody is clicking on — makes a click
on an Angular page. And the actual page they want to display is
an AngularJS page. How would you handle that? How would you implement that? So I’ve got some code to show you. This is just one way I did it. So with the Angular route configuration, you
can have something called a fallback route. So if you see at the end it’s got the two
stars, that means if it doesn’t match any other route, it will hit the fallback route
at the end. When that route gets hit, we inject in the
iFrame component. What’s the iFrame component? That’s it. That’s the HTML for the iFrame component. That’s it, and I set the source value to a
URL. It has the simple CSS. I tried it a bunch of different ways. The best thing with an iFrame is just make
it take up maximum width and size of the page. Otherwise you start dealing with scroll bars
and other ugly little issues like that. And that’s the actual code. Hope you can see it. The key line is there. So when the iFrame component is initialized,
I get the — I subscribe to the activated route. I get the URL. I then have to do some manipulation to convert
this URL to something the AngularJS application understands. Then I have to do this weird thing. In Angular, it sanitizes stuff by default. You kind of have to desanitize it, to allow
that URL to be used in an iFrame. And then do something called listen for fallback
routing events, which I’ll go into later on. So basically, what’s going on? So I click in my Angular application. It searches for the URL in the route configuration. It can’t find it. So it goes to the fallback route. The fallback route adds in the iFrame component,
which just puts the right URL to show that URL from the AngularJS application. Okay. That’s from Angular to AngularJS sorted. What about the other way around? What about if you click on an AngularJS page
and you want to display an Angular page. You can’t use the same solution, because you’re
not trying to put an iFrame in a page. You’re trying to communicate from child iFrame
to parent iFrame. You can do something reasonably similar. This is the route configuration for the AngularJS
application. You can see at the bottom I’ve got an “otherwise”. That’s how you fall back with UI router. And in there… Oh. And in there, I call… Parent, post message. The parent is a special variable that becomes
available to you when you are being iFramed in. So it’s the parent iFrame. So I call parent, and I compose the message
to the parent iFrame. And I’m passing an object, which is the URL
that I want to navigate to. Okay? And then in the iFrame component, and the
listen to fallback events, I’m basically listening for that event, and when I get it, I’m calling
navigate by URL on my own router. Now, there’s code for this that you can get
in the slides later on. So don’t worry about it too much. Basically what am I doing? When I click in the AngularJS application,
it’s hitting the fallback route. The route isn’t there. And then passing a message using the post
message API to the parent application and it’s then navigating with the URL. So that’s how we handle shared routing between
these two applications. How do we handle shared state? Now, there’s so many different ways of handling
this problem. I’m showing you what I think is the simplest
way to handle this problem. And that’s basically: As long as you keep
your application, your legacy and your Angular application in the same domain, it means it’s
sharing the same cookies and it’s sharing the same local storage. If it’s sharing the same cookies and using
authentication, if you’re logged in on one, you’re logged in on both, and you can use
the same data between the applications. And you can use something called storage events
to communicate between the two different apps. I’ll show you in a second. So basically whenever I set that counter,
I’m also setting it in local storage, under counter. Okay? And then… I can just listen to storage events. So storage events are sent every time you
save something to local storage. So I can listen to that in my Angular application,
and if it’s… If local storage is saving something called
counter, I can then set the value locally. So that’s also how you can synchronize data
between — like, if you had the same application open in multiple tabs, same domain, you can
synchronize it within all those tabs, in local storage. And all the iFrame is — is a separate application. So you can synchronize between the iFrame
and the parent iFrame, with local storage and storage events. So that’s it, basically. So in summary, with the ng-module approach
— it’s a great approach. It really is. I’ve used it successfully several times. But it comes with certain issues. It comes with other baggage you need to deal
with, and your architecture need to be at a certain standard for it to work. So whenever I’m working with clients on this,
I usually say: Well, if you’re already using pretty modern modules, and if you’re already
a pretty decently architected AngularJS application, you’ve got a decent chance of migrating with
the ng-module. If you’re using really old modules or you’ve
got really pretty bad architecture — when I look back at some of my old AngularJS applications,
it’s pretty interesting — then the ng-module approach takes a little bit too much effort
to be worthwhile. So with the iFrame solution, you don’t have
to deal with the old baggage. They’re two completely separate applications. They can have their own modules. And you can just leave your wonderful AngularJS
application with all of its emit and broadcast, cleave it exactly how it is, and migrate it
to a beautiful new Angular application. But I think one of the really interesting
things about the approach I’m talking about here is that you can use it with other frameworks. Okay? It’s just an iFrame. I’ve just migrated from AngularJS to Angular. It doesn’t matter. I can migrate from Vue to Angular. From React to Angular. From Angular to Vue. Hm. (laughter) But that’s one of the things that’s really
exciting about this approach. I’m kind of getting a little bit tired of
all the framework wars that are going on in the world right now. If we can live in a world with multiple cultures,
multiple religions, multiple beliefs, then I think we can live in a world with three
JavaScript frameworks. So that’s it. If you want to find out more information,
I have a blog post about this on my blog, codecraft.tv. I have all the code, all the samples, everything
you need. And yeah. If you want to… You can follow me… Oh! Okay. Take a picture. You don’t have to take a picture. Because if you follow me on Twitter, which
you can do here, then I’ll be posting them on Twitter later on. It has all the links on the end of the slides
as well. But yeah, thank you very much! (applause)>>Thank you! Follow the jawache. Thank you for clearly that up, by the way. I did not know. I always thought it was jawashy or something
like that. Jaw-ache. It’s even better. Thank you again! So next up we have… What do we have? We have Aysegul. Are we gonna talk about what’s going on in
the next room? No, because you guys can’t leave. Do you need to get set up or anything? Awesome. Well, I have some Angular Jeopardy, while
Aysegul gets set up. What is the only external dependency in Angular? Shout it out. Rxjs. Very good. It should be “what is Rxjs”. But I won’t fault you for it. Let me see. I have another one. This is the command to run the Typescript
compiler from the command line. Oh, you guys are good. You must do Angular. All right. This is the module that contains important
decorators like ng-module and component. What is Angular Core! Ooh! Are you guys learning? Okay. This decorator should be used to decorate
every service in Angular. Man, you’re good, right here. And another one over here. Let’s see. Ah. Server-side rendering in Angular? Server-side rendering in Angular. I didn’t hear it. Universal! You’re cheating! I’m just kidding. Okay. Let’s see. I have another one. Oh, I’ll do one off the top of my head. So the new operators in RxJS are called this. Leadable. Or pipable. Did you know? True or false: Leadable operators are tree-shakeable. True! That’s right. Shake those trees. Shake shake shake, shake shake shake! I can waste time up here. Okay. Let’s see. This is the RxJS operation that you use to
convert data in a stream. Oh, there we go. Let’s see. What else? Angular Core. Oh, okay, here is one. This is the feature of Angular which allows
you to format data in a template. Format data in a template. The feature of Angular. Somebody said it. Pipes. Pipes! He is the father of Angular. Mishko! There we go. And apparently, the spirit of Angular is? There you go. He’s not the stepson of Angular? I thought he was the stepson of Angular. Man. I think that’s all I have. But you guys are gonna get this amazing lady,
who actually has blue hair. I just have… And purple, right? Blue and purple? Yeah. I know. But I didn’t dye my hair, because I can’t
do that. You can do it. But I can’t.

 

Leave a Reply