From JavaScript mess to Cleaner Code - Step 4-2 - Angular2

January 18, 2017    JavaScript Development Aurelia jQuery Clean Code

From JavaScript mess to Cleaner Code - Step 4-2 - Angular

In Step 4.1 we looked at using the web framework Aurelia to reduce code. Another popular and valid choice is Angular2 (3, 4… it’s just Angular). I have many links I’ve been collecting and will be continuing to post as I learn more.

My code can be found on GitHub. I created the project using the Angular Command Line Interface (CLI). This made it quick and easy to create project (ng new) and serve it on port 4200 with WebPack (ng serve). See my readme on that GitHub project for more info.

This project may be a good start to comparing it with Aurelia, but more complex features is needed to give a through comparison. The choice between the two depends on the experience and preferences of the team. Angular2+ is not an update to Angular1, but a complete re-write. I haven’t used Angular1 much, but there are many differences. It doesn’t look easy to migrate from 1 to 2 (though maybe it’s not so bad as mentioned on DotNetRocks and Auth0) so it is still an opportunity to compare to Aurelia, React, Vue, Svelete, Ember, etc. All the options get overwhelming quickly, so you may just have to pick a horse and run with it.

I recommend starting with the Angular QuickStart or walking through the Angular Tutorial to get started learning Angular.

Get Running

  1. Clone my code: git clone https://github.com/aligneddev/JQueryToAurelia
  2. navigate to the angular directory cd src\angular2\AngularSolarEnergy
  3. open it in VS Code (or your choice) code .
  4. npm install
  5. ng serve
  6. Open a browser where it’s served from http://localhost:4200.

main.ts

The app gets bootstrapped and setup in the main.ts.

    import './polyfills.ts';
    import { platformBrowserDynamic } from [email protected]/platform-browser-dynamic';
    import { enableProdMode } from [email protected]/core';
    import { environment } from './environments/environment';
    import { AppModule } from './app/';

    if (environment.production) {
        enableProdMode();
    }

    platformBrowserDynamic().bootstrapModule(AppModule);

App.Module

Angular’s main setup is in the AppModule (that’s what I called it and set it in the bootstrapModule(AppModule) in main.ts). In order for Angular to know about your components you need to declare them. You also need to manually specify the imports as well. The CLI tool helps get components setup and placed in here ng g component my-new-component speeds things up a lot.

@NgModule({
declarations: [
    AppComponent,
    EnergyComponent,
    EnergyDetailsComponent,
    NotesComponent
],
imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    // us the in memory data service => InMemoryWebApiModule.forRoot(InMemoryDataService),
    AppRoutingModule,
    NgbModule.forRoot()
],
providers: [{ provide: IEnergyDataServiceToken, useClass: EnergyDataJsonService }],
bootstrap: [AppComponent]
})
export class AppModule { }

Components

Components are the building blocks of Angular. “You write Angular applications by composing HTML templates with Angularized markup, writing component classes to manage those templates, adding application logic in services, and boxing components and services in modules.” “A component controls a patch of screen called a view.”

Components are based on the W3C Web Components ideas (in my opinion) and allow us to write composable smaller pieces and put them together. This helps us create more maintainable and reusable code.

There is a tied html file as declared in the templateUrl of the @Component decorator.

Notice the dependency injection capabilities by looking at the constructor. My Example: Energy Data Details view. This is navigated to when you click on a data row.

import { Inject, Component, OnInit } from [email protected]/core';
import { ActivatedRoute, Params } from [email protected]/router';
import { Location } from [email protected]/common';
import 'rxjs/add/operator/switchMap';

import {IEnergyDataService} from 'app/energy/energy-data-service.interface';
import {IEnergyDataServiceToken} from 'app/energy/energy-data-service.token';

import EnergyDataDto from '../energyDataDto';
@Component({
selector: 'app-energy-details',
templateUrl: './energy-details.component.html',
styleUrls: ['./energy-details.component.css']
})
export class EnergyDetailsComponent implements OnInit {
    public selectedEnergyData: EnergyDataDto =  new EnergyDataDto();
    constructor(@Inject(IEnergyDataServiceToken) private energyDataService: IEnergyDataService,
        private route: ActivatedRoute,
        private location: Location) { }

    ngOnInit() {
        this.route.params
        .switchMap((params: Params) => {
            return this.energyDataService.getEnergyDataByIdAndYear(params['id'], params['year']);
        })
        .subscribe(data => {
            this.selectedEnergyData = data;
        });
    }

    public goBack() {
        this.location.back();
    }
}

Component Html with DataBinding

You’ll see {{}} for string interpolation, [ngModel], (click)="clickMethodInComponent, and more. Don’t forget “Bananas in a box for 2 way binding [()]”.

<section>
    <h1>Details for {{selectedEnergyData.countryName}} in {{selectedEnergyData.year}}</h1>
    <ul>
        <li><b>Quantity:</b> {{selectedEnergyData.quantity}}</li>
        <li><b>Year:</b> {{selectedEnergyData.year}}</li>
        <li><b>TransactionName:</b> {{selectedEnergyData.commodityTransactionName}}</li>
        <li><b>Unit:</b> {{selectedEnergyData.unit}}</li>
    </ul>
    <br />
    <button (click)="goBack()">Back to Main</button>
</section>

Data Service

A service is a good choice for interacting with an HTTP/REST API endpoint.

Tutorial on Promise and Observables

The InMemoryWebApiModule.forRoot(InMemoryDataService), option in AppModule (tutorial link) is a cool feature to have dummy static data for testing or data before your API is ready. You can also look at my code and pull the data from a .json file.

Note: the @Injectable attribute allows EnergyDataService to be injected into other classes. It’s registered in AppModule.

My Example: energy-data.service.ts

@Injectable()
export default class EnergyDataService implements IEnergyDataService {
    private yearOptions: string[] = [];
    private energyDataCache: EnergyDataDto[] = [];
    protected apiUrl = '/api/';
    protected solarUrl = 'solar';
    constructor(private http: Http) { }
    ...
    public getEnergyData(option: string): Promise<EnergyDataDto[]> {
        return this.http.get(this.getEnergyDataApiUrl(option)).toPromise().then((response) => {
            this.energyDataCache = this.parseEnergyDataResponse(response);
            return this.energyDataCache;
        }).catch(this.handleError);
    }
    ...
    private handleError(error: any): Promise<any> {
        console.error('An error occurred', error); // for demo purposes only
        return Promise.reject(error.message || error);
    }
    ...
}

Forms

There is a lot to learn for forms in Angular. I created a quick notes example, but you’ll learn much more from their tutorial.

Testing

I was even more overwhelmed by the Angular Testing options and capabilities. I collected a lot of articles and I still don’t have a good handle on all the options and how to approach it. It appears that there are many opportunities to write tests that avoid UI tests and do a good job covering your code.

If you are interested in Test Driven Development (TDD), the best information I found was Angular2 TDD in ES6. videos cover 2.0.0-beta.13, so beware that there are a lot of changes since then.

The Good

  • Angular is extremely popular and even though v2+ is a different product, developers are moving to the new version and the community is growing. This all leads to good community support, tutorials, talks and documentation.
  • The framework takes care of routing, binding, module loading, etc. It makes it easy to make a Single Page App (SPA).
    • We didn’t have to decide on RequireJs vs something else, or KnockoutJs or JQuery. We just follow the Aurelia teams direction.

Many of these are similar to my “Goods” in Step 4.

  • Components everywhere!
  • The CLI makes it easy to start development and package with webpack.
  • The testing capabilities are impresive, if not overwhelming.
  • TypeScript! I don’t think I’d do much JavaScript work without TypeScript. It has been that valuable to our project.
  • It feels similar to using Knockout, but without all the confusing parenthesis.
  • You get to depend on the team or community behind the framework.
  • You get to learn a new approach to web development!

The Bad

  • There is a lot to learn to get proficient in Angular. Even if you know Angular1, it is very differnt in 2+.
  • Figuring out how to do testing took me a lot of time for research (1 full day) and I still don’t have it figured.
  • There are many frameworks, how do you choose?
  • Using any framework requires learning. Are you up for it? Are your teammates?
  • More bytes for the client to download than you could possibly do on your own.
    • Of course, that argument goes away quickly if you are using jQuery or other libraries.
  • You have to depend on the team or community behind the framework

Please checkout the other steps in my investigation of Cleaner Code in JavaScript projects.

There should be a ReactJs one coming soon. A friend did an implementation in React.

Bonus

I’ve had ups and downs with Angular so far. Here’s how I graphed it on the white board. My experiences with Angular so far



comments powered by Disqus