Implementing an Angular Hybrid App Part 1

Before the release of Angular.io, React, Vue, the most used framework for SPA was without any doubt Angulajs.

We used it for small and large applications, because of its modularity that help us to organize the code in different logic modules.

But now with the release of new frameworks, we have some questions: how I can update my old Angularjs applications to a new framework?

First of all, this question seems to be important because Angularjs will no longer supported from July 2021, so from that date, any new bug won’t be fixed.

The big problem is not update a small application where, with some weeks we’ll be able to give the new application to the business, but the refactoring of enterprise applications like ones where I’m working on, with hundreds of javascript file with controllers, factories and so on.

So, after some investigations I decide to choose Angular.io because with this framework, starting from and old Angularjs application, we can implement an Hybrid one where both the frameworks coesists in the same application.

Another good point is that this process could be done gradually, with propedeutic steps that prepare the application to the integration with the new framework.

I’ve organized my work in four main activities:

  • Angularjs application refactoring
  • Typescript modules implementation
  • Angular.io and Webpack configuration
  • Implementation of the application bootstrap

About the first step, in order to make our application “hybrid”, there are three checks that we have to do and concerns:

  • Have single component per file
  • Avoid using of $scope
  • Using explicit annotations

Single component per file

This step is not mandatory but high recommended to improve testing and readability of the application.

Basically, we could be in a situation like this:


(function (window, angular) {
'use-strict';
angular.module('blogsModule', ['ui.router', 'odataResourcesModule'])
.config(function ($stateProvider) {
$stateProvider
.state('home.blogs',
{
url: '/blogs',
templateUrl: 'app/main/blogs/blogs.html',
controller: 'blogsCtrl'
})
.state('home.blog',
{
url: '/blog/:id',
templateUrl: 'app/main/blogs/blog.html',
controller: 'blogsDetailCtrl'
});
})
.factory('blogsService', function ($http, odataGenericResource) {
return new odataGenericResource('odata', 'Blogs', 'Id');
})
.controller('blogsCtrl', function ($scope, $state, blogsService) {

$scope.new = function () {
$state.go("home.blog", { id: null });
};

$scope.detail = function (id) {
$state.go("home.blog", { id: id });
};

$scope.Blogs = blogsService.getOdataResource().query();
})
.....

We have a single file that contains the module declarations and components.

This is not the best situation and we’ll see later when we’ll converting in typescript modules that is better to have different files for any component.

So we can reorganize the code above in three different js files:

blogsModule.js


(function (window, angular) {
'use-strict';
angular.module('blogsModule', ['ui.router', 'odataResourcesModule'])
.config(function ($stateProvider) {
$stateProvider
.state('home.blogs',
{
url: '/blogs',
templateUrl: 'app/main/blogs/blogs.html',
controller: 'blogsCtrl'
})
.state('home.blog',
{
url: '/blog/:id',
templateUrl: 'app/main/blogs/blog.html',
controller: 'blogsDetailCtrl'
});
})

})

blogsService.js


(function (window, angular) {
'use-strict';
angular.module('blogsModule')

.factory('blogsService', function ($http, odataGenericResource) {
return new odataGenericResource('odata', 'Blogs', 'Id');
})

})

blogsController.js


(function (window, angular) {
'use-strict';
angular.module('blogsModule')

.controller('blogsController', function ($http, odataGenericResource) {
.....
})

})

With this organization, the step about converting javascript files in typescript will be simplified because we’ll converting smaller different files.

Avoid using of $scope

The $scope, one of the main objects used in Angularjs applications is no longer supported in Angular.io, so we have to removed it from the controllers; fortunately we can use a different syntax to obtain the same result, that is binding the view objects with the controller.

Suppose that we have a controller like this:


.controller('blogsCtrl', function ($scope, $state, blogsService) {

$scope.new = function () {
$state.go("home.blog", { id: null });
};

$scope.detail = function (id) {
$state.go("home.blog", { id: id });
};

$scope.Blogs = blogsService.getOdataResource().query();
})

Removing the $scope means that alle the public methods and objects will be referred to the controller itself, so the result will be:


.controller('blogsCtrl', function ($state, blogsService) {
var vm = this;

vm.new = function () {
$state.go("home.blog", { id: null });
};

vm.detail = function (id) {
$state.go("home.blog", { id: id });
};

vm.Blogs = blogsService.getOdataResource().query();
})

So we are using this keyword to refer to the controller and to associate the methods.

The other step is declare explicitly the controller in the html page:

<div ng-controller="blogsCtrl as ctrl">
<div class="col-lg-10">
<button type="button" class="btn btn-primary" ng-click="ctrl.new()">New</button>
</div>

<div class="col-lg-2">
<input type="text" class="form-control" placeholder="Search" ng-model="ctrl.query" required>
</div>
.....
</div>

Simply we have a div that wrap the entire html code and refer the controller, and we can use the alias to call the variables and methods binded.

Using explicit annotations

When we define the dependencies of angularjs components it’s extremely useful use implicit annotation that has a more compact and less error prone sintax:


.controller('blogsCtrl', function ($scope, $state, blogsService)

In order to use this syntax we have to configure an explicit task in the javascript minification process, otherwise angular would not be able to resolve those dependencies and the application would brake; an example of these tasks is grunt-ng-annotate, that can be used of course with grunt.

Unfortunately with webpack things are different and if we used the implicit annotation we have to return back to the explicit one:


.controller('blogsCtrl', ['$scope', '$state', 'blogsService', function ($scope, $state, blogsService)

Summary

About this activity there could be another step about the conversion of directives in angularjs components that could become reusable in the Angular.io framework; this is an expensive activity and in my opinion a good alternative is search new components to be use in the new framework, that they fits better and will be in many cases more efficient that the older ones.

We have completed the step of javascript refactoring and in the next post we can go ahead to implementing the typescript modules.

You can find the source code here.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: