Showing posts with label Angular CLI. Show all posts
Showing posts with label Angular CLI. Show all posts

Sunday, May 13, 2018

Implementing the Bulma CSS Framework Using Sass Compilation in an Angular CLI Project

I recently stumbled across the Bulma CSS framework. I'd never heard of it before, but it's been out now for over two years, and is generated considered as a pure CSS (no JavaScript) alternative to Bootstrap.

After looking at the documentation, I decided I wanted to try styling my Angular 5.x vadacl demo app with Bulma, and that I wanted to be able to take advantage of the fact that it could be customized via Sass (even though I've never worked with Sass before).

After some missteps and some research, I discovered that integrating Bulma with the Angular CLI is pretty easy.  Here's what I ended up doing:

  1. In my vadacl demo project, I ran the CLI command that updates the CLI configuration to use SCSS ("SCSS" is the latest syntax for Sass) files to generate the styles for the project:  "ng set defaults.styleExt scss".
    • When creating a new Angular CLI project, you can use the flag "--style=scss" on the "ng new" command to do the same thing.

  2. I then installed Bulma using npm: "npm install bulma --save-dev".

  3. Next, I created a "sass" folder in the assets folder of my project and created a "variables.scss" file.  In that file, I added the line:

    $primary: #694979;

    ...to change the color value of the $primary Sass variable used in Bulma.

  4. Then I created the "styles.scss" file in the src folder of my project, and in that file I added the following lines:

    @import "assets/sass/variables.scss";
    @import "../node_modules/bulma/bulma.sass";

    Initially it seemed counterintuitive that I would set my own values for the Bulma variables ahead of the import for Bulma, but Bulma is designed to accept those variable values if they've already been declared, and otherwise fall back to using the Bulma defaults.

  5. Then I opened the "angular-cli.json" file in my project and made sure the "styles" property array included "styles.scss".

That's all I needed to do.  I confirmed that everything worked by adding a few buttons to the home view of my app with Bulma styles:
<p>Bulma test</p>
<a class="button is-primary">Primary</a>
<a class="button is-link">Link</a>
<a class="button is-info">Info</a>
<a class="button is-success">Success</a>
<a class="button is-warning">Warning</a>
<a class="button is-danger">Danger</a>
...and I could see the effect of my change to the $primary color (which is normally a light sea green.



Resources:


Wednesday, August 10, 2016

IntelliJ, Angular CLI, and Indexing

As I started working on my Angular CLI-managed Angular 2 project, I discovered that making code changes while Angular CLI was either serving my application or waiting to re-execute unit tests would cause my IntelliJ IDE to start re-indexing my project files.  Each indexing run took several minutes and during that time IntelliJ was slow to respond to my attempts to edit and interact with the code files.

I solved this performance issue by selecting the "Project Structure" / "Project Settings" menu item, selecting "Modules", and marking the following folders as "Excluded" on the "Source" tab:

  • dist
  • tmp
Those two folders are created and updated by Angular CLI automatically while testing and serving the application: there's no benefit in having the IDE index them.

Monday, August 8, 2016

Learning Angular 2: Creating My First Sandbox Web Application

While there is still a lot out there for me to read regarding Angular 2, I tend to learn by coding and solving problems.  Even though there are a few aspects of Angular 2 that are in flux at this time (like forms), I feel that I can start writing an application without much fear that I'd have to go back and redo things because the API has changed.

So I've created my first "sandbox" Angular 2 application where I can practice writing Angular code and figure out ways to accomplish specific application tasks with Angular 2.  I'm going to keep a copy of the code up on GitHub and release milestones in my development so that I have a historical picture of the development and so I can potentially backtrack and create different solutions to a given problem.  Plus, it will allow anyone to pull down a tagged version on their own machine to look at the code.

My first sandbox is an application called "GuildRunner".  My plan is that it will be an application for managing a fictional collection of trade guilds, and so I can use it to exploring dealing with common application issues like authentication and authorization, data relationships, and searching.  But I wanted to start by simply creating the foundation for the application structure and getting it up and running.

So I started by creating the repo in GitHub, and then cloned the repo into a new IntelliJ IDEA project via the IntelliJ option for creating projects via GitHub.  I then opened a command prompt in the project directory and invoked the Angular CLI command "ng init" to create the starting project files: I was pleased that the command let me decided whether or not to overwrite the README.md file cloned from GitHub.  Another nice thing about the CLI-generated file I hadn't noticed before:  the .gitignore file is configured to ignore the IntelliJ-specific files as well as the node_modules and typings folder during commits, which is nice.

At that point, I had a basic, single-component app that I could run with the "ng serve" command of Angular CLI.  But I wanted to use the in-memory web API (at least initially) to provide the data for the application as if it was interacting with a server and a database, so I needed to reconfigure the application to utilize that feature.  The details of that reconfiguration ended up as a separate blog post: Adding the In-Memory Web API to a SystemJS-based Angular CLI Application.

In the Tour of Heroes example of using the in-memory web API, the mock data was defined/written out within the InMemoryDataService createDB() method.  Since I plan on creating a fair amount of mock data, I created a "db" folder under the "app" folder that would house all the modules that would export the data collections.  I then created my first bit of mock data: a single version record.


//src/app/db/version.ts

let version = [
  {id: 1, name: '0.0.1'}
];

export { version }

...and provided that to the InMemoryDataService via an import:


//src/in-memory-data.service.ts

import { version } from './db/version';

export class InMemoryDataService {
  createDb() {
    return { version };
  }
}

The purpose of the version record was to have some data to display on the main application page that would confirm that the in-memory web API was working (and also confirm the version of the sandbox application I was working with). So with the Tour of Heroes code as a guide, I created a VersionService to retrieve the version data and a VersionComponent to display it:


//src/app/version/version.service.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import 'rxjs/add/operator/toPromise';

@Injectable()
export class VersionService {

  private versionUrl = 'app/version'

  constructor( private http: Http ) { }

  getVersion() {
    return this.http.get(this.versionUrl)
      .toPromise()
      .then(response => response.json().data )
      .catch(this.handleError);
  }

  private handleError(error: any) {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
  }
}

//src/app/version/version.component.ts

import { Component, OnInit } from '@angular/core';
import { VersionService } from './version.service';

@Component({
  moduleId: module.id,
  selector: 'app-version',
  templateUrl: 'version.component.html',
  providers: [
    VersionService
  ]
})
export class VersionComponent implements OnInit {

  versionNumber = '-.*.-';

  constructor( private versionService: VersionService ) { }

  ngOnInit() {
    this.versionService.getVersion().then( versions => this.versionNumber = versions[0].name );
  }

}

<-- src/app/version/version.component.html -->

<p><strong>Version:</strong> {{versionNumber}}</p>

Then (after adding Bootstrap to the project and adding some Bootstrap layout containers), I added the VersionComponent as a sub-component of the AppComponent:


//src/app/app.component.ts

...
@Component({
  ...
  directives: [
    VersionComponent
  ]
})

<-- src/app/app.component.html -->
<div class="container">
  <div class="row">
    <div class="col-md-10">
      <h1>{{title}}</h1>
    </div>
    <div class="col-md-2 text-right">
      <app-version></app-version>
    </div>
  </div>
</div>

Once all of that was done, I could run my application using "ng serve" and a moment after the page loaded I could see the version number.

I finished up this version of the sandbox by updating the current set of unit and end-to-end (e2e) test files such that they would pass. Previous experience with e2e testing let me update the single e2e test pretty easily, but getting the minimalist unit tests to work took some trial-and-error, and I don't yet have a clear sense of how to set up the unit tests such that the component under test has all the dependencies (or mocks of the dependencies) it needs.

The release of the GuildRunner sandbox that contains the application foundation code and the changes described above can be found on the GuildRunner GitHub repo as release 0.0.1:

https://github.com/bcswartz/angular2-sandbox-guildrunner/releases/tag/0.0.1 

Instructions for running the sandbox on your own machine via Angular CLI can be found on the main page of the GitHub repo.

Thursday, August 4, 2016

Adding the In-Memory Web API to a SystemJS-based Angular CLI Application

8/6/2016 EDIT: On 8/2/2016, the Angular CLI was updated to reflect the fact that the CLI was being refactored to use Webpack instead of SystemJS.  Currently, an npm install of Angular CLI will still give you a version that uses SystemJS, and the following instructions apply to that SystemJS version.

As of version 1.0.0-beta.10, the Angular CLI tool does not provide an option for generating a base Angular 2 application that includes the in-memory web API, which is a tool that lets developers simulate the return of data from HTTP calls.  I figured adding the in-memory web API to my CLI-generated application was just a matter of mimicking how the HTTP lesson in the Tour of Heroes tutorial did it, but it was a bit more involved than that. Here's how you do it.

(From here on out, I'm going to abbreviate "in-memory web API" as IMWA for the sake of brevity. Someone needs to come up with a shorter, cooler name for this tool.)

First, you add the IMWA as a dependency in the package.json file:


"dependencies": {
  ...
  "angular2-in-memory-web-api": "0.0.14"
}

...and then run "npm install" from the command prompt in the directory containing the package.json file to download the IMWA node module (I tend to delete my entire "node_modules" folder first just to make sure everything installs fresh).

Then you add the neccessary imports to your main.ts file and use those imports in the bootstrap() method:


import { XHRBackend } from '@angular/http';

import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
import { InMemoryDataService }               from './app/in-memory-data.service';

import { HTTP_PROVIDERS } from '@angular/http';

...

bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  { provide: XHRBackend, useClass: InMemoryBackendService },
  { provide: SEED_DATA, useClass: InMemoryDataService }      
]);

Then create a in-memory-data.service.ts file in your src/app directory with some temporary placeholder data:


export class InMemoryDataService {
  createDb() {
    let tempData = [
      {id: 1, name: 'foobar'}
    ];

    return { tempData };
  }
}

Up to this point, all of the setup is nearly the same as it was for the Tour of Heroes tutorial, but the final changes needed are CLI-specific.

When you use the CLI's "ng serve" command to compile and run the application on your local machine, the CLI generates a "dist" directory with all of the necessary files to execute the application. The "dist" directory consists of:

  • The index.html and global configuration files (main.js and system-config.js) in the root of the "dist" folder.
  • The "app" folder which contains the rest of the Angular 2 files specific to your application, from the main component file on down.
  • A "vendor" directory that contains the files from the various node modules needed to run the application, such as the core Angular 2 library files.

The IMWA needs to be included as a separate folder under that "vendor" directory in order for the IMWA to be available to your application.

The "angular-cli-build.js" file in the root of your project filespace (in the same directory as the package.json) controls which node modules make it into the build (the "dist" folder). Add the IMWA to the array of modules like so:


vendorNpmFiles: [
  ...
  'angular2-in-memory-web-api/*.+(js)'
]

The "*.+(js)" syntax ensures that all of the ".js" files in the IMWA node module are copied to the appropriate folder ("angular2-in-memory-web-api") under "vendor" (at this time, there are no ".js.map" files in the IMWA module).

Finally, you need to make sure the IMWA module in the "vendor" directory is loaded by SystemJS along with the regular Angular modules by adding the IMWA to the package configuration in the "system-config.ts" file in the "src" directory of your project:


/** Map relative paths to URLs. */
const map: any = {
  'angular2-in-memory-web-api': 'vendor/angular2-in-memory-web-api'
};

/** User packages configuration. */
const packages: any = {
  'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};

That should do the trick: run your app using "ng serve", open up the browser console, and confirm that there are no 404 error messages regarding the IMWA.

Sunday, July 31, 2016

First Impressions of Angular CLI

Before creating a demo Angular 2 project of my own from scratch, I decided to play with Angular CLI, the command line tool provided by the Angular team to help streamline Angular 2 development.

There are a number of posts and articles out there about Angular CLI, so I'll only share a few personal observations:

  • The "--help" option for displaying documentation for the overall list of commands or individual commands is well executed:  much more useful and readable than most command line tool documentation.

  • I really like the "dry-run" option provided with the commands that generate the application skeleton and config files.  It lets you see a list of the files and folders that would otherwise be created by the command without actually creating them, giving you an idea of what to expect.

  • The application skeleton structure is a bit different from the structure used in the Quick Start and Tour of Heroes tutorial, moving the "app" directory under an "src" directory.  It does this to make room for a "dist" directory parallel to the "src" directory where it can output runtime files for testing and environment-specific distribution builds.  I found it interesting that it places the "main.ts" file in that "src" directory instead of the "app" directory.

  • I liked how the default development distribution build retains the separate .js and .js.map files for the project code files, while the production build concatenates those files and generally packages your assets to make them more efficient.

  • The "ng generate" command is most useful for generating new components.  The generated component contains the appropriate @angular/core imports and @Component() metadata, contains an empty constructor method, and implements ngOnInit.  Option flags used with the generation command can customize certain aspects of the generated component, such as whether the component uses external HTML and CSS files (the default) verses inline template and inline styles, and whether all of the component assets are bundled in a separate directory.  The other files you can generate with the command are mostly empty shells (although generated service files do include the @Injectable() decorator).

  • I like that the generated components use the module.id technique to handle relative pathing for the templateUrl and styleUrls properties.

  • The CLI documentation refers to the types of files you can generate with "ng generate" as "blueprints".  I hope that means that there will one day be an option to add your own personal blueprints into the mix.

  • It's interesting that "ng test" starts by creating a development build in the "dist" folder before performing the tests...presumably because the tests are run against that folder.  I'm sure there's a reason for doing it that way (making sure everything works after packaging?), but it makes the startup time for testing slow.  On the flip-side, once it's started it watches for file changes and re-tests on the fly, so if your coding process involves running unit tests in the background all the time the start-up time penalty is a one-time cost.  I also wonder what the implications are for performing unit testing via testing tools in your IDE:  would those IDE tools also need to test the build files rather than the .js files in the "app" folder?

Overall, I really like the tool so far, and I look forward to seeing it evolve alongside Angular 2.