3 posts tagged

Angular

Tic-Tac-Toe Using Angular and MobX

Adam Klein

MobX solves the problem of state management in frontend apps, in a declarative, simple and performant way. It differs from other popular solutions by removing a lot of boilerplate, and allowing you to work with mutable data and OOP.

The best way to explain how it works is via a code example.
Our demo application is a tic-tac-toe game.

Full code and demo.

The basic flow in MobX is:

  • observables – define trackable attributes
  • computed – define derived state
  • actions – change the state
  • reactions – react to the change (update UI)

Plain objects

A store in MobX is just an object. It saves only the minimal state of our application. In our case, a 3X3 array representing the board.

class Game {  
  @observable board:string[][];
}

The @observbale decorator tells MobX it should track access to the board attribute. Note that this has nothing to do with rxjs’ observable, except for the name.

Computed values

Tell me your board, and I shall tell you who the current player is...

In our example, we will need a few other attributes of the game.
For example, who the current player is, who is the winner, how many moves left, etc. All of these values can be derived from the board. In MobX, this is called computed values:


  // Count the total number of cells that have a value
  @computed get moves():number {
    return this.board[0].filter(cell => cell).length +
      this.board[1].filter(cell => cell).length +
      this.board[2].filter(cell => cell).length;
  };

  // If 'moves' is even, then it's X turn, otherwise O
  @computed get currentPlayer():string {
    return this.moves % 2 ? 'X' : 'O';
  }

Computed values are just declarative functions that calculate derived properties. They are only recalculated if needed, and if they are being observed somewhere.

Actions

Changing the state in MobX is simple. It’s simply setting attributes on plain objects:

@action play(i,j) {
  this.board[i][j] = this.currentPlayer;
}

@action resetGame() {
  ...
}

The @action decorator tells MobX to run this function in a transaction. A transaction is a block a of code that recalculates computed values only when the block finishes, and not immediately when observables are set.

Connecting to Angular

This is actually the easiest part.

1. Inject the store:

import { GameService } from 'app/services/game.service';

@Component({
  ...
})
export class ControlsComponent {  
  constructor(private game:GameService) { }
}

2. Use the store inside the component as a regular object:

template: `
    <div *mobxAutorun>
      <h1 *ngIf="game.winner">{{ game.winner }} has won the game</h1>
      <button (click)="game.resetGame()">Reset Game</button>
    </div>
  `

The mobx-angular library offers a *mobxAutorun directive that automatically observes the values that we use inside our template.

The ControlsComponent uses the winner computed value, and invokes the resetGame action. Whenever a board cell is changed, MobX will recalculate the winner attribute, and update the component – telling it to re-render.

  • If we use OnPush change detection strategy, we can gain extremely high performance.
  • When the component is destroyed, the observer is automatically disposed.
  • Under the hood, this directive uses MobX autorun function.

How does this magic work?

MobX wraps each @observable value with custom getters and setters. When we access this.board[0][1] for example, MobX adds this observable to a dependency tree of the current computed value. Then, when we run something like: this.board[0][1] = ‘X’, MobX checks the dependency tree and recalculates all the relevant computed values. Then, it runs all the reactions that are dependent on all observables and computed values that changed.

Where do I start?

A good place to start is to read mobx and mobx-angular documentation.

Then, create a POC on your existing app. Choose a specific page of the app and move the state to a MobX store to see how easy and straightforward it is.

Good luck!

Angular   MobX

SVG in Angular 2

Adam Klein

If you just tried using SVG naively in Angular2, you will get weird errors, such as:

main.bundle.js:27686 EXCEPTION: TypeError: Cannot set property r of #<SVGCircleElement> which has only a getter in [radius in [email protected]:26]

Here are a few tips for building SVG components in Angular2.

attr.X

In order to bind to SVG element attributes In angular 2, you must prefix them with attr., e. g.:

<svg>  
  <circle [attr.r]="myRadius" cx="50" cy="50"></circle>  
</svg>

svg: prefix

Angular needs to know whether the component’s namespace is HTML or SVG. If the first tag of the component’s template is svg, angular guesses the SVG namespace.

If you want to make inner SVG components that don’t have the SVG tag, you must prefix them with ‘svg:’.

<svg:circle [attr.r]="myRadius" cx="50" cy="50"></svg:circle>

[attribute] selector

SVG is very strict and cannot accept elements it doesn’t know. For example, this is invalid markup:

<g>  
  <myCircle> <!-- INVALID! -->
    <circle r="40" cx="50" cy="50"></circle>
  </myCircle>
</g>

So, to make inner components of SVG you must use an [attribute] selector:

@Component({
    selector: '[myCircle]',
    template: '<svg:circle [attr.r]="myRadius" cx="50" cy="50"></svg:circle>'
})

And use it as an attribute (usually on svg or g elements):

<g myCircle></g>
The result HTML would look something like:

<g myCircle>  
  <circle r="40" cx="50" cy="50"></circle>
</g>

In case you want to see it live: plunkr

Angular   SVG

Understanding Angular 2

Boris Dinkevich

What does <div [x]=“3” (y)=“z()”> mean? The usual reaction for developers taking a look at the new Angular2 is: WTF? Luckily, it will take us only a few minutes to understand what the developers of Angular2 were thinking.

If you haven’t tried it yet, the easiest way to play around with Angular2 today is either online with codepen.io or locally with a simple playground.

Let’s get started!

Component({  
  selector: 'app-main'
})
.View({
  template: `<h1>Hello</h1>`
})
.Class({
  constructor: function() {}
});

Woohoo!

In & Out

In Angular2 the whole “directive” <-> “world” communication method was simplified. A directive (now called ‘component’) can bind to “stuff” and it can emit events that the world can listen to.

We will initially only be playing around with the “template” part as that’s where the weirdness happens.

Component({  
  selector: 'app-main'
})
.View({
  template: `<h1>Hello</h1>`
})
.Class({
  constructor: function() {
    this.msg = 'world';
  }
});

The backtick character in ES6 allows us to finally have multi-line strings in JS.

How would we go about connecting the msg variable to a div back in jQuery land? Update the property of that element with something like:

$('div').textContent = msg;

Well Angular2 is exactly the same, only with it we can bind them – make sure that any change to msg changes the div’s content:

.View({
  template: `<div bind-text-content="msg"></div>`
})

Well, that makes sense, but could we...

.View({
  template: `
    <div 
      bind-text-content="msg" 
      bind-style.background="'red'">
    </div>
  `
})

OMG! We can just run around binding HTML attributes directly to expressions! (Did you notice that we used ‘red’ inside “‘? Yep, any expression, ‘r’ + ‘e’ + ‘d’ would do as well.

Events

We got binding covered, so lets talk about Events. What happens when an input field changes?

<div  
  bind-text-content="msg" 
  bind-style.background="color">
</div>  
<input on-keyup="color = $event.target.value" />

Again, familiar jQuery ground here, and as you can guess, any event can be bound to this way.

Of course in real code we will see things like:

on-change="changeColor()"

Wait, that was not Angular 2...

You are right, the samples all look different, and that is due to “syntactic sugar”. Instead of writing bind-text-content you can just do [text-content] and instead of on-keyup use (keyup).

<div [text-content]="msg" [style.background]="color"></div>  
<input (keyup)="color = $event.target.value" />

Much better.

Real Components

While a div is a cool component and all, real components will behave in a very similar fashion. They will have a bunch of “properties” that can be passed to them and they will emit a bunch of “events”.

Creating Components will be the second part of this tutorial

Variables

Angular 2 adds another idea to its templating, “Local Variables”. We can create a variable that exists only in the template’s scope and make our code a bit cleaner.

<div var-my-div>Hello</div>  
<input (keyup)="myDiv.style.background = $event.target.value" />

When we used var-my-div a new myDiv variable was created that references the component itself. We could later use it in our keyup function.

myVar will not be visible inside the Component’s Class instance!

With some syntax sugar we can turn that var-my-div into #myDiv

<div #my-div>Hello</div>  
<input (keyup)="myDiv.style.background = $event.target.value" />

What about those * thingies?

Lets start with a simple example

<template [ng-if]='true'>
  <span>Hello</span>
  <p>World</p>
</template>

Sidenote – Injections...

The above should have failed when trying to run in the simple playground. The reason being – standard directives also need to be imported and injected to be used. For our example, it would mean adding the following:

At the top of the file:

import { NgFor, NgIf } from 'angular2/angular2';

And inside the @View decorator:

directives: [NgFor, NgIf]

Back to business

<template [ng-if]='true'>
  <span>Hello</span>
  <p>World</p>
</template>

Nothing fancy, and easy to understand. We are creating a ShadowDOM element and passing it to the NgIf directive. How about NgRepeat (Now called NgFor)?

<template ng-for bind-ng-for-of='[1,2,3]'>
  <span>Hello</span>
  <p>World</p>
</template>

The main difference is that there is a new input that NgFor sets on the template – which is the list on which to iterate over.

How do we gain access to the value and index of each row? The Angular2 way! The NgFor directive creates two new variables that we can use to bind to:

<template ng-for  
          bind-ng-for-of='[1,2,3]' 
          var-v='$implicit' 
          var-i='index'>
  <span>Index: {{ i }}</span>
  <span>Value: {{ v }}</span>
</template>

Lets add some Angular2 sugar:

<template ng-for  
          [ng-for-of]='[1,2,3]' 
          #v='$implicit' 
          #i='index'>
  <span>Index: {{ i }}</span>
  <span>Value: {{ v }}</span>
</template>

That is a bit long and ugly – that is why when the template has one root element (i.e. is wrapped with a single div ), we can turn things around:

<div template="ng-for #v of [1,2,3]; #i=index">  
  <span>Index: {{ i }}</span>
  <span>Value: {{ v }}</span>
</div>

You probably noticed the strange “ng-for #v of [1,2,3]; #i=index” syntax. Angular2 does some pretty complex parsing for the template directive. We will talk about it in depth in a future post.

To make things even prettier – The special character * tells Angular2 that we are using a template and which directive to use to manage it:

<div *ng-for="#v of [1,2,3]; #i=index">..  
  <span>Index: {{ i }}</span>
  <span>Value: {{ v }}</span>
</div>

What else?

Many different and interesting things, but this is the base you need to know to start using directives.

In the next chapter we will talk about building directives.

A big thank you to Yoni Weisbrod for proofreading this post.

More comments and discussion on HackerNews

Angular