Gentle Intro to Angular for Frustrated Python Devs
Being new to Angular, I still have pretty fresh eyes on my experience with the learning curve. As a Python Developer with object-oriented experience, these are the things I wish someone would have told me.
Your First (and Potentially Last) Angular App
Do you remember writing your first Python program? It was probably a joyous moment involving the creation of simple one-liner called hello.py
:
print("Hello World!")
So simple! One file, one line, and it runs on any version of Python 3.
Now, for your first Angular app… you’re familiar with HTML, JavaScript, and Typescript, and you have your comfy socks and 2 hours of free time. After glancing at some tutorials, you probably decided to dive right into the deep end by typing ng new
. If you’ve used React in the past, you were probably expecting a self-contained .js/.ts file ready to be served by your favorite back-end template manager.
Go on, type ng new my-first-app
. I dare you.
After answering a few innocuous questions and pressing enter, your console explodes into a fury of compilation and file creation. 10 seconds and 31 files later, you have your “Hello World!” Angular app created.
Sadly, this is where most tutorials begin and enthusiasm ends.
Your Second First App
Angular is a behemoth. It has a built in test suite, git repo, linter config files, Angular prod/dev setup files, and even prepares your Node environment. Maybe future versions will do your taxes. Later down the road this all might be super-great, but for someone who just wants a Hello World example this can be an anxiety inducing affair.
Fortunately, most of this stuff can be ignored. Some of it can even be removed.
For something a little more lightweight, try ng new my-second-app --minimal --skip-git
. This constructs our new project without testing, git, and keeps the template .html
files embedded in the actual script. It’s not quite “barebones,” but I hope by now you know what Angular isn’t a “barebones” affair.
Using the power of ignoring things, our app is now only 2 files!: app.module.ts
and app.component.ts
.(And if we really wanted to, we could combine them). As for the others files, don’t worry about them for the time being. They’re there for a reason. You’ll find that Angular is like a Flask or Django or Kivy app, or any other framework that executed by some mysterious back end forces. You define the functions and classes, but something else runs it. The files we’re ignoring control that behavior.
Simple App Structure
app.module.ts — Project Definitions
Most general purpose programming languages have a similar format. Some imports, some class declarations, and some functions. Angular is no exception; if you’ve done object oriented programming in Python or have worked with framework like Flask, this should start to look familiar to you.
Take a look at app.module.ts
, which defines the structure of our entire app:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';import { AppComponent } from './app.component';@NgModule(
{
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap:[AppComponent]
}
)
export class AppModule { }
No HMTL here, just some imports and an empty (but decorated) class. Going over the imports:
BrowserModule
— I don’t know what this does, but so far I don’t need to.NgModule
— This is the decorator used in the code, and part of Angular core. Much like Flask in Python, decorators are how the real magic works in Angular. This tells Angular that this the starting point of your app.AppComponent
— This is your app! Or, it’s a component in your app at least. Likeapp.module.ts,
it’s located in the/src/app
directory. It provides ourAppComponent
class, which we will modify to change our website.
Next in app.module.ts
is some real code.
This looks like a mess (at first), but on closer examination as least it’s familiar. The class itself doesn’t do anything. The decorator @NgModule
turns this empty class into an Angular Module and defines declarations
, imports
, providers
, and bootstrap
. Decorators in TypeScript are pretty similar to those in Python; they simply modify the object/function they’re decorating. In this case, the decorator adds some Angular-specific properties and makes it look like an Angular Module. It’s possible to write this without decorators, but I think this is much cleaner. Why Angular chose decorators over inheritance, or instances of their own predefined classes? Who knows. Anyways:
declarations
— This is a list of every component in our Angular app. We only have the one right now.imports
— These are the imports or packages required by the components.providers
— These are classes which Angular will prepare and inject into components. In the “Bonus: Constructors” section of this article,CookieService
would be included as a provider.bootstrap
— The entry point for your app. In this case, it’sAppComponent
as defined inapp.component.ts
.
app.module.ts
doesn’t have anything too spectacular; primarily it defines the scope of our project. For the actual website, take a look at app.component.ts
.
app.component.ts — Your Website
import { Component } from '@angular/core';@Component(
{
selector: 'app-root',
template: `<div>{{title}}</div>`,
styles: []
}
)
export class AppComponent {
title = 'my-second-app';
}
Different file, same story: decorated class. Instead of@NgModule
, however, there is@Component
. Components are the actual pieces being displayed on your website. The decorator attributes are:
selector
— This is the HTML tag which will be replaced with a component. In this case it’sapp-root
, which means Angular will find<app-root></app-root>
inindex.html
one the directory above— yeah we just figured out what one of those files does!templateUrl
— This is the HTML template for this Component. If you did--minimal
, this will be actual HTML (which I replaced with<div>{{title}}</div>
for brevity. If you didn’t, this will be a template.html
file.styleUrls
— Full disclosure, I am not a UI/UX person (yet?), and I despise working with CSS. But here’s where you’ll find it.
An Angular Component is just an Object
So the HTML is in a separate file/blob, which is defined in the decorator… then what does the actual class do?
Runs our code of course! Right now it’s just one line: title = my first app
. Let’s imagine for a second that Angular was written for Python. In this case (full disclosure, I’ve never used a Class decorator in Python) our code above would look like:
from Angular.core import Component@Component(
selector="app-root",
templateURL="""<div>{{title}}</div>""",
styleUrls=[]
)
class AppComponent():
title="my-second-app"
A Python-Angular app would be surprisingly similar. Comparing TypeScript to Python, it becomes obvious thattitle
is a just TypeScript class property ( or Python class attribute). And here’s an important note: our HTML template has access to class properties.
Take another look at the “minimal” template text and notice the {{title}}
thing embedded in the HTML. It’s probably obvious that this displays the page title, and that the curly braces are responsible. But what’s going on exactly?
- Curly braces in Angular indicate one-way-binding from the app to the view. If
AppComponent.title
changes, so will{{title}}
. Get used to hearing about “binding”; this simply means one thing (e.g. the angular app) causes another thing (e.g. the webpage) to change. - So why doesn’t
{{title}}
needthis.
orself.
orAppComponent.
in front of the property name? Because the template lives inside the class! The decorator injects this as a Class property. So in fact, the only variables the HTML can see are it’s own class properties!
Angular Components Have Class Methods
So above was an example of one-way-binding being used to update the view from the app. The opposite one-way-bind would be the view updating the app via an event.
Let’s create a Button which invokes a class method and changes the title to “Clicked Title”:
import { Component } from '@angular/core';@Component(
{
selector: 'app-root',
template: `<button (click) = "newTitle()">{{title}}</button>`,
styles: []
}
)
export class AppComponent {
title = 'my-second-app'; newTitle() {
this.title = 'Clicked Title';
console.log('Title was changed!');
}
}
In our imaginary Python Angular language, this might look like:
from Angular.core import Component@Component(
selector="app-root",
templateURL="""
<button (click) = "newTitle()">
{{title}}
</button>""",
styleUrls=[]
)
class AppComponent():
title="my-second-app" def newTitle(self):
self.title="Clicked Title"
print("Title was changed!")
Great! In our HTML blob, we can access class methods! And class methods, of course, can access Class properties! Notice the ()
surrounding the click
property. The parenthesis let Angular know that it’s looking for a Class method to make an event out of— in this case it’sAppComponent.newTitle()
.
Bonus: Constructors (aka __init__())
Often libraries and functions need to be imported into components. Take for example CookieService
, which handles saving and retrieving variables as Cookies. Instead of pip install ngx-cookie-service
, it’s npm install ngx-cookie-service
.
If we install this library and incorporate this into our example:
import { Component } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';@Component(
{
selector: 'app-root',
template: `<div></div>`,
styles: []
}
)
export class AppComponent {
title = 'cookie-test';
constructor( private cookieService: CookieService ) { } newTitle() {
this.title = 'Clicked Title';
console.log('Title was changed!');
this.cookieService.set('title', this.title);
this.title = this.cookieService.get('title'); }
}
In our imaginary Python Angular project, this might look like:
from Angular.core import Component
from ngx_cookie_service import CookieService@Component(
selector="app-root",
templateURL="""<div></div>""",
styleUrls=[]
)
class AppComponent():
title="my-second-app" def __init__(self, cookieService: CookieService):
self.cookieService = cookieService def newTitle(self):
self.title = "Clicked Title"
print("Title was changed!")
self.cookieService.set('title', self.title)
self.title = self.cookieService.get('title')
constructor
is simply __init__
in Python! In the Angular example cookieService
would be a provider in app.module.ts
which would be injected into AppComponent
with the help of TypeScript types. In the Python example, I would have to use type hinting to indicate to our imaginary Angular back end that this should be a CookieService object.
Also, you are correct: the example above doesn’t do anything useful.
Same But Different
Python and Angular are obviously entirely different beasts, but all object-oriented programming is similar to some extent. In every case, you have a class, you have attributes, and you have class methods. And all Angular is is just object-oriented-programming.
If there is anything you should take away from this, just remember: An Angular Component is just a Class!
Further Reading
With this new mindset, why not take another look at the Tour of Heroes Angular tutorial?
Also be sure to brush up on: