Sunday, 18 November 2018

Understanding angular application folder structure

Background

In the last post, we saw how to write a simple "Hello World!" application -
In this post, I will try to explain the directory structure and importance of various files and folders in the angular project that we generated.


NOTE: This is for angular 7. Folder structure and file names might be different in previous versions of angular.

 Understanding the angular application folder structure

 If you see the previous post the directory structure generated was as follows -


We go try to see all the files and folders in the above picture and see what each does.

e2e

e2e stands for end to end. This consists of your end to end integration tests. these tests are run as if it is a real user testing but are actually simulated one. This includes opening a browser, navigating to the page, clicking on UI elements etc.


This uses a package called protractor. You can find more details about protractor in https://www.protractortest.org/#/.

node_modules

If you are familiar with node environment this requires no special introduction. Angular app is essentially a node package. You can see there is a file called package.json in the root folder of your generated application. It defines dependencies of your node package. So that when you run "npm install" these dependencies are installed. These dependencies go under a folder called "node_modules" which is in the same folder as package.json. 


src/app

This folder is the source folder of your angular application. It contains all the source code that you write for your angular app to render. This will have files corresponding to your module, components, templates, css/saas files etc. The angular app will have at least one module and component.

src/assets

 As the name suggests this folder should store all your static assets like images, icons etc.

src/enviroments

This folder contains the configuration for environments you define. By default, it would have 2 environments -
  1. environment.prod.ts : For production environment
  2. environment.ts  : For development environment

favicon.ico

This is the icon that gets showed when your browser loads your page.


index.html

This is the main HTML file that loads. You can see it has tag -
  • <app-root></app-root>
This tag corresponds to the selector of bootstrap component(app.component.ts) that is defined in your root module(app.module.ts).


main.ts

This is our main type script file. Here we can bootstrap our module as -

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
 

polyfills.ts

This type script file contains scripts that are needed by angular since the javascript feature may not be available in the browser. So basically it fills the gap to provide the features of JavaScript that are needed by Angular application and the features supported by your current browser.

For example, IE may not support a File or a blob object. In this file, you can add your custom definitions.

style.css

This file contains your common styles that are global to the application. It could be a css or a saas file depending on what you choose during creation of your angular application.

karma.conf.js

This file is used to store the setting of Karma. Karma is used for unit tests while protractor that we saw above is used for end to end tests. For details see https://karma-runner.github.io/latest/index.html

NOTE: Karma is a great tool for unit testing, and Protractor is intended for end to end or integration testing. This means that small tests for the logic of your individual controllers, directives, and services should be run using Karma. Big tests in which you have a running instance of your entire application should be run using Protractor. Protractor is intended to run tests from a user's point of view - if your test could be written down as instructions for a human interacting with your application, it should be an end to end test written with Protractor.

test.ts

This file is required for doing the testing setup.  This file is required by karma.conf.js and loads recursively all the .spec and framework files.


angular.json

This is the main file from which angular configuration is loaded. If you see this file it will have reference to all files - mian.ts, indes.html etc. All configuration needed for your angular application to build and run resides here.


NOTE: In previous versions of angular this file was called angular-cli.json.


package.json

As mentioned in node_modules section, package.json is a descriptor file for any node project. It defines project name, it's dependencies etc.

tsconfig.json

This has configurations related to your typescript compiler. Your typescript compiler will look at these settings and converts type script to javascript that the browser understands.

tslint.json

This file has the liniting configuration. Rules that developers must follow to maintain the codebase in a standard way.

.gitignore

This is a file that git understands and whatever you add inside this file will be ignored by git for version control.




Related Links

Angular Hello World example

Background

In some of the earlier post, we saw Angular JS which is the older version of angular. 

Current LTS version of angular is angular 6 (at the time of this writing). We will however be using angular 7 which is the active angular release.  In this post I will show you how to write a simple angular Hello World app. In the subsequent posts we will try to understand all files in the angular app and their purpose, angular routing, data binding etc.


Angular CLI

For generating angular app and it's components we are going to use Angular CLI (command line interface). You can see the wiki here. You need to have npm (node package manager) installed for this. If not please refer to my earlier posts on NodeJS(Links in the related section at the bottom).

To install Angular CLI execute following command -
  • npm install -g @angular/cli 
 NOTE: You may have to run it with sudo of you are seeing permission issues. Once installed you can verify the installation by typing

  • ng --version



 Once angular CLI is installed you are all set to create your 1st angular app.



Angular Hello World example

Now let's create a new angular app, to begin with. To create a new angular application execute following command -

  • ng new angulardemo
 It if of format
  • "ng new projectname"

 You will now be asked for choices like would you like to add routing to the application. Would you be using CSS, SASS etc? For now, select Yes for routing and CSS as an option. We will see this later. For our current demo, these are not really essential.




 Once done you can go into the project directory and run.
  • ng serve -o
 This should build your app and open it in your browser.




 This is what the default application looks like. Now it's time to make some changes of our own. To do that open your application in your favorite IDE. I prefer using Visual Studio code. If you ae using this you can just open your project by going into the project directory and running -
  • code .
The directory structure looks like below -



 As you can see there are bunch of files here but not to worry. We will see each in time. In this post, we will see how we can make changes and deploy our own code.


The entry point is a file called angular.json. This file has all the configuration information used for building and developing your angular app. Look at the following part-


          "options": {
            "outputPath": "dist/angulardemo",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }

You can see the important files we would have to check. You can see the index file is "src/index.html", the corresponding type file is "src/main.ts". Similarly, the CSS file is "src/styles.css". There are other files but not to worry about it as of now. We will just worry about these files for now.

Now let's go to our index.html and here you can see the following tag -
  • <app-root></app-root>
Now let's try to understand what would get rendered in this tag.  As I mentioned before the main file in "main.html" and corresponding typescript file is "main.ts". If you go to main.ts you will see -

import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));


This basically instructs to bootstrap a module called AppModule which is located at './app/app.module'. So go to this file. And you will see the following content in it -


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule { }


This is the main module of your Angular app. A module can have multiple components. An angular can have multiple modules as well. You can see in the above example it imports 2 modules BrowserModule, AppRoutingModule. You can see where it is imported from. The important part to see here is the bootstrap part because that is the component that actually gets rendered on loading an angular app. So lets go to ./app.component file.


import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  title = 'angulardemo';
}


Here you can see AppComponent is a component. Also, you can see it's selector is "app-root" which we saw in index.html. You can also see the HTML file and CSS file this component is using to render itself. So let's go its HTML file './app.component.html'. You can see this has some HTML code that got rendered when we ran "ng serve" command above.

Time to make some changes. Remove this code completely and replace it with following code.


<div class="hello-world">
  <h1>Hello World!</h1>
</div>


Notice we have added a class for the div called "hello-world". Let's add this class to our CSS file - './app.component.css'

 .hello-world {
    font-style: italic;
}


That's it. If you already have "ng serve -o" command running your changes should get reflected immediately in the browser. If you killed the command, run it again and you should see the changes reflecting in the browser.



You can see in above picture that "Hello world!" comes in h1 tag and in italics()as our class defined it.





Related Links

Saturday, 27 October 2018

Keep learning, Keep sharing!

A wise man once said -
 If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.


I crossed 40k reputation on Stack Overflow(SO).  And that accounts to 2544 rank in the world!




It has been almost 5 and a half years since added my 1st answer to the site. And today 1,130 answers, 144 questions and a people reach of ~21.4 million later here I am going back to where it all started. I still remember the 1st answer that I wrote - it was downvoted just because an admin read it wrong and when he realized it, he was the 1st person to upvote the answer and that's how the journey began. I am taking this opportunity to stress a very important point - Knowledge increases by sharing.



I am sure every developer has relied on some SO answers for finding directions to get their issues resolved. The reason I am sharing this today is to stress the point that if it has helped you, it would probably help a hundred others around the globe. So make sure to upvote that answer. If the answer did not work out for you but it helped you in the correct direction go ahead and add it as a new answer or a comment. Innovation is not always in doing different things, it can be doing things differently. Never hold back anything thinking the answer or question might be silly. You already know the outcome if you don't, so why not give it a try and see how it goes. If it's stupid but it works, it isn't stupid - as simple as that. Worst case scenario - someone correct you which not only helps you understand it better but also everyone else who is in the same boat. I personally consider this the best scenario - You don't learn anything if you think you are always right!



Personally other than technical learning aspect of it, this has helped me a lot in terms of interviews, communicating with fellow developers and expanding personal reach. I get this question a lot during the technical interview - "Why do you have so many questions answered as compared to the questions asked with just 5 years of experience?". What I have learned during the course of last 5 years is that the same question has many different angles and like I said before if you are facing a problem there would be many others facing the same issue. It could be different OS, different library version, different runtime, a different flavor of language etc. I have always tried to add more content to the answers based on the issue I have faced and today based on the shown stats there are around 21.4 million developers who were in the same boat. That's a win-win scenario!


I will conclude this post by saying - "Keep learning, Keep sharing!". Good luck :)

Sharing few more stats - Just for fun :)







PS: If you need my help in getting traction to your question in terms of upvote or bounties feel free to reach out to me at - opensourceforgeeks@gmail.com.

Tuesday, 16 October 2018

Lerna Tutorial - Managing Monorepos with Lerna

Background

Lerna is a tool that allows you to maintain multiple npm packages within one repository. There are multiple benefits of using such an approach - one repo, multiple packages. This paradigm is called monorepo (mono - single, repo - repository). You can read more about monorepo -
To summarise pros are -
  • Single lint, build, test and release process.
  • Easy to coordinate changes across modules. 
  • A single place to report issues.
  • Easier to set up a development environment.
  • Tests across modules are run together which finds bugs that touch multiple modules easier.
Now that you understand what a monorepo is, let's come back to Lerna. Lerna is a tool that helps you manage your monorepos. In this post, I am going to show you a complete tutorial of how you can use lerna to manage a custom multi package repo that we will create.





Lerna Tutorial -  Managing Monorepos with Lerna

First of all, you need to install lerna. Lerna is a CLI. To install it you need to execute following command -
  • npm install --global lerna
This should install lerna in your local machine globally. You can run the following command to check the version of lerna installed -
  • lerna -v
 For me, it's 3.4.1 at the time of writing this post. Now let's create a directory for our lerna demo project.  Let's call it lerna-demo. You can create it with the following command -
  • mkdir lerna-demo 
Now navigate inside this directory
  • cd lerna-demo
Now execute following command -
  • lerna init
This should create a basic structure of monorepo. Take a look at below picture to understand it's structure.


 It does following things -
  1. Creates packages folder. All packages go under this.
  2. Creates package.json at the root. This defines global dependencies. It has the dependency on lerna by default.
  3. Creates lerna.json at the root. This identifies lerna repo root.
 Now let's go to packages folder and start creating our packages. We will then see how we can link them and use. So navigate to packages directory.
  • cd packages

Package - AdditionModule

Let's create a package that takes care of addition. Let's call it AdditionModule. Create a directory for this inside packages folder and execute following commands -
  • mkdir AdditionModule 
  • cd AdditionModule 
  • npm init -y
This should create a file called package.json inside AdditionModule directory. Now in the same folder create a file called index.js in the same folder and add following content to it -

module.exports.add = function(x,y){
    return x + y;
}


Save the file. This basically exposes add method to any other package that would have a dependency on this. Your 1st package is done. Let's create one more package for subtraction.

Package - SubtractionModule

 Run similar commands inside packages folder -

  • mkdir SubtractionModule
  • cd SubtractionModule
  • npm init -y
 Now in SubtractionModule folder create a file called index.js and add following code to it -

module.exports.subtract = function(x,y){
    return x - y;
}


Save the file. This basically exposes subtract method to any other package that would have a dependency on this. Now let's create a package that would have a dependency on  AdditionModule and SubtractionModule and can use add and subtract functions.

Package - Calc

Our final package - let's call it Calc would have a dependency on  AdditionModule and SubtractionModule packages. So let's create the package 1st -

  • mkdir Calc
  • cd Calc
  • npm init -y
Now create a file called index.js and add following content to it -

var add = require('AdditionModule');
var subtract = require('SubtractionModule');

var sum = add.add(2,3);
var diff = subtract.subtract(3,2);

console.log("Sum: " + sum + " Diff: " + diff);

Save the file. Now open package.json to add our dependencies. Edit package.json to add following content to it -

  "dependencies": {
      "AdditionModule": "1.0.0",
      "SubtractionModule": "1.0.0"
   },


So your entire package.json looks like -

{
  "name": "Calc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
      "AdditionModule": "1.0.0",
      "SubtractionModule": "1.0.0"
   },
}

Now you are all set up. Your directory structure should look like below -



Now it's time to use lerna to link packages. Go to lerna-demo directory and execute the following command -
  • lerna bootstrap
 This should do all linking for you. It would create node_modules directory in Calc package and add symlinks to AdditionModule and SubtractionModule. Your directory structure would now look like -


Now you can simply run calc index.js as follows -

  • node packages/Calc/index.js
And you should get expected output -




The important thing was the linking part that lerna does for you so that you do not have to worry about it.


For complete details on all available commands see - https://github.com/lerna/lerna

Related Links




Sunday, 14 October 2018

What is the purpose of Node.js module.exports and how do you use it?

Background

If you have ever used nodejs and npm you must have encountered usage of module.exports syntax. Or perhaps just exports? If you do have encountered this that you might be aware that it exposes a certain functionality of the code so that other outside code can reference and execute it. Though this is a short version of what exports syntax does, in this post we will see a lot more of how it behaves and some examples to help us understand this better.










What is the purpose of Node.js module.exports and how do you use it?

The module.exports object is created by the Module system. module.exports is the object that's actually returned as the result of a require call. exports is just an alias to module.exports. So you could use either. There are some things that you would need to take care if you are just using exports but more of that later. For now, let's just take a simple example and see how it actually works.

First, create a file called calc.js  with the following content -

var add = function(x,y) {
return x + y;
}

var subtract = function(x,y){
return x - y;
}

module.exports = {
add  : add,
subtract: subtract
};



This code is simply defining two functions -  add and subtract and exporting them. Notice how these methods are declared as exports. We will revisit this back in a moment. Now let's try to use this code in a separate node code.

Now create another file - Let's call it demo.js. Now in this file add following code -

var calc = require('./calc.js');

console.log("2 + 3 = " + calc.add(2,3));
console.log("3 - 2 = " + calc.subtract(3,2));

And finally, run the demo.js file as -

  • node demo.js
It should give you the expected results -




If you understood above logic you must have got a basic idea of how exports work. To revisit our earlier statement we said - "module.exports is the object that's actually returned as the result of a require call." In this case, it returns a map of add and subtract which point to corresponding functions that you can invoke.

NOTE: Notice the "./" in the require statement. This is required to tell node that it is a local package.

Another way to use the same module.exports would be as follows -

module.exports.add = function(x,y) {
return x + y;
}

module.exports.subtract = function(x,y){
return x - y;
}


And if you now run demo.js again you would still see the same output. Both are just different ways to expose your code outside.

NOTE: Note that assignment to module.exports must be done immediately. It cannot be done in any callbacks. So following will not work -


var add = function(x,y) {
return x + y;
}

var subtract = function(x,y){
return x - y;
}

setTimeout(() => {
  module.exports = { add : add, subtract : subtract };
}, 0);



This will fail with below error -

TypeError: calc.add is not a function
    at Object.<anonymous> (demo.js:3:31)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3



So make sure your exports are done immediately and not in any callbacks.



Now let's come back to the exports keyword that we said is just an alias to module.exports. Let us rewrite the code with just exports now.  Let's say your code is as follows -


exports.add = function(x,y) {
return x + y;
}

exports.subtract = function(x,y){
return x - y;
}


Now run demo.js and you should still get your desired output. Like I said earlier exports is just an alias to exports.module. Now lets out 1st approach with just exports -

var add = function(x,y) {
return x + y;
}

var subtract = function(x,y){
return x - y;
}

exports = {
add : add,
subtract: subtract
}


And if you run demo.js again you will get an error -

TypeError: calc.add is not a function
    at Object.<anonymous> (demo.js:3:31)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3

So why did this happen? This brings us to another crucial fact -

If you overwrite exports then it will no longer refer to module.exports. The exports variable is available within a module's file-level scope, and is assigned the value of module.exports before the module is evaluated. So if you overwrite exports it is no longer an alias to module.exports and hence it no longer works.

So make sure you do not overwrite exports variable. It is always safer to just use module.exports.

To summarize this in a single picture module.exports work as follows -




Related Links

t> UA-39527780-1 back to top