11 posts tagged

AngularJS

AngularJS Dependency Injection And Dolphins

Adam Klein

Have you ever found yourself asking: should I use a value? a factory? a service? a provider?! Ask yourself 3 questions:

  1. Do I need to inject it in the config phase?
  2. Does it have dependencies?
  3. Am I a dolphin?

Then reduce which type of service you need to use:

  • Service <= not needed in config phase, has dependencies
  • Value <= not needed in config phase, has no dependencies
  • Constant <= needed in config phase, has no dependencies
  • Provider <= needed in config phase, has dependencies
  • Factory <= You are a dolphin

Notice that if you answer yes to the third question you don’t need to ask yourself the other questions – optimizing the algorithm :)

The reasoning behind

Angular modules have a config phase, that runs before the app is bootstrapped. During that phase you can only inject constants or providers. You cannot inject a service or a value:

angular.module('app', [])
    .config(function(Constant, Provider) {
      // ...
    })
    .controller(function(Service, Value, ...) {
    })
  ;

Constants and values can’t inject dependencies. Thus, they are simpler to define:

angular.module('app', [])  
  .constant('PI', Math.PI)
  .value('Temperature', 27.5)

In most cases, there’s no real justification for using a value over a constant. The main advantage I found, is that you can override it in tests, in order to mock it.

Why not factory?

To understand why you never need to use a factory, you need to know how the injector instantiates our services.

For a service, it’s something like this:

service = new Service(dependency1, dependency2, ...)

For a factory, it’s something like this:

factory = Factory(dependency1, dependency2, ...)

In Javascript a constructor function can ignore the fact that it was called with new operator, and act as a regular function, returning whatever it likes. For example:

function Service() {  
  return { value: 3 };
}
var service = new Service();  
// service.value === 3;

function AnotherService() {  
  this.value = 3;
}
var anotherService = new AnotherService();  
// anotherService.value === 3;

However, if it hasn’t been called with new, you can’t use it as a constructor, but you have to return a value. For example:

function Factory() {  
  this.value = 3;
}
factory = Factory();  
// factory === undefined;

Thus, you can replace all your factories with services, and everything will work the same way, but not the other way around.

*disclaimer:
There’s only thing you can’t return from a constructor function invoked with new is a primitive value (string, integer, etc.). So the only case it will make sense to use a factory is for defining primitive values that require dependencies.

Hope you enjoyed the guide!

AngularJS

Using React with AngularJS

Boris Dinkevich

How does ReactJS work in AngularJS? The best way to follow the tutorial is by opening the initial codepen and adding/modifying stuff as you go along.

First codepen: http://codepen.io/borisd/pen/waVJZW

AngularJS on a page

Let’s start with a super simple application. A single directive that renders a header:

angular.module('app', [])
  .directive('pics',() => {
    return { 
      template: `<h2>AngularJS is here!</h2>`
    };
  });

And add the HTML we need to load it up:

<div id='app1'>
  <pics></pics>
</div>

Instead of using the usual ng-app=“app” way, we will use the following (it will become clearer in a bit)

angular
  .bootstrap(document.getElementById('app1'), ['app']);

We are basically getting a div on the page and mounting the app on it.

Updated code: http://codepen.io/borisd/pen/RPXVQG

Let’s get crazy

In the codepen above you will notice we don’t have one but rather _three_, divs with AngularJS running inside. Contrary to what some developers think, you can have AngularJS bound to a single container and run multiple apps on the same page!

Enter ReactJS

Let’s create a simple ReactJS component (add it right above the AngularJS module declaration)

const Pic = React.createClass({
  render() {
    return React.createElement('h2', {}, "ReactJS is here!");
  }
});

It should look familiar; it’s just a ReactJS component that renders a header.

How do we load it? Just replace the bootstrap code for app3 with:

React.render(React.createElement(Pic), document.getElementById('app3'));

Meditate on what happened for a minute.

ReactJS did the same as AngularJS and just bound itself to a container on the page.

Here is the updated code: http://codepen.io/borisd/pen/GJVWbJ

Embedding

So what does ReactJS need to load? Yep, a container. And if we want it to work inside AngularJS let us try adding a container into the directive... change the *pic* directive to:

angular.module('app', [])
  .directive('pics', () => {
    return { 
      template: `
        <div>
          <h2>AngularJS is here!</h2>
          <div class="inside">inside</div>
        </div>`
    };
  });

And instead of loading ReactJS on #app lets load it inside of AngularJS:

React.render(
  React.createElement(Pic), 
  document.getElementsByClassName('inside')[0]
);

Magic! – http://codepen.io/borisd/pen/NqQpZd

Remove the jQuery

We have ReactJS component loading _inside_ of an AngularJS directive... but *getElementsByClassName* doesn’t feel right. The proper way would be to have a nice directive take care of this for us.

What do we need inside it? A div for ReactJS to load on:

.directive('react', () => {
  return {
    template: '<div></div>'
  }
})

Now let’s move the ReactJS render code into it, say inside the link function:

.directive('react', () => {
  return {
    template: '<div></div>',
    link: (scope, element, attrs) => {
      React.render(
        React.createElement(Pic), 
        document.getElementsByClassName('inside')[0]);
    }
  }
})

How do we access the *div* that we created in the template? Easy: element[0]. The final link function is then:

link: (scope, element, attrs) => {
  React.render(React.createElement(Pic), element[0]);
}

Let’s add the new directive into our AngularJS *pic* directive by changing the template to:

template: `
  <div>
    <h2>AngularJS is here!</h2>
    <div class="inside" react>inside</div>
  </div> `

Notice the *react* directive usage in the inner div.

Much better! – http://codepen.io/borisd/pen/ZGgKzx

Can’t have Pic running around

Our next problem will appear when we try to divide the project into files – we need to have *Pic* available to the *react* directive. We can always use import or require but AngularJS has a nicer way – “Dependency Injection”.

We are going to wrap our ReactJS component into an AngularJS factory:

.factory('Pic', () => {
  return React.createClass({
    render() {
      return (React.createElement('h2', {}, "ReactJS is here!"));
    }
  }); 
})

Thats it! Just inject Pic into the *react* directive and the DI magic takes over.

The code – http://codepen.io/borisd/pen/yNmbNv

A directive per component?

Now we have both a factory for a component and a directive that renders it – too much code. Lets make our *react* directive be more versatile.

Instead of using it like:

<div class="inside" react></div>

It will be much nicer to have:

<div class="inside" react="Pic">inside</div>

Notice how we pass the Component name to the directive

Let’s modify the *react* directive, so our current implementation looks like this:

link: (scope, element, attrs) => {
  React.render(React.createElement(Pic), element[0])
}

To make it work we first need to figure out the name of the Component we want, and that already sits in the *attrs* we have in the link function: attrs.react (the second is the name of the directive).

Now we need to use Dependency Injection to get the actual Component. For that we will use the $injector service:

.directive('react', ($injector) => {
  return {
    template: '<div></div>',
    link: (scope, element, attrs) => {
      const component = $injector.get(attrs.react);
      React.render(React.createElement(component), element[0])
    }
  }
})

A nice clean reusable directive: http://codepen.io/borisd/pen/NqQjGj

What about data-binding?

Now that we have ReactJS fully “DI”ed and a nice way to use it in AngularJS let’s talk about sharing data between the two.

The first method is quite straightforward, but first let’s create some data to share by adding the following service:

.service('PicsService', function($interval) {
  this.data = { counter: 0 };

  $interval(
    () => this.data.counter += 1,
    Math.random() * 200 + 100
  );
})

Using arrow function in services will not work as angular needs to bind it to the object created by *service()*

The service adds a counter and increments it continuously (so we can see data-binding is fully functional).

To test it, inject it into our *pic* directive and add the following controller:

controller: ($scope) => {
  $scope.data = PicsService.data;
},

Then we can see it working by just adding {{ data.counter }} inside the template.

What will we do for the ReactJS component? Let’s try the same:

.factory('Pic', (PicsService) => {
  return React.createClass({
    render() {
      return React.createElement('h2', {}, 
        "ReactJS is here! ", 
        PicsService.data.counter);
    }
  }); 
})

Alas, we only got the initial value of the counter, there are no updates. To make ReactJS play in AngularJS’s playground, we need to use the digest cycle. The simplest being $watch.

To use $watch we need a scope; a factory doesn’t have one. So lets use the $rootScope.

One of the ways to use $watch is to have the first parameter be a function that the digest cycle will call every time, and compare its return value to the previous one. If they differ – that means we need to do an update.

In our case we will monitor the PicsService:

$rootScope.$watch(
   () => PicsService.data.counter,
   (newVal) => this.setState({ counter: newVal })
 );

The full code for the factory:

.factory('Pic', ($rootScope, PicsService) => {
  return React.createClass({

    componentWillMount() {
      this.state = { counter: 0 };
        
      $rootScope.$watch(
        () => PicsService.data.counter,
        (newVal) => this.setState({ counter: newVal })
      );
    },
           
    render() {
      return React.createElement('h2', {}, 
        "ReactJS is here! ", 
        this.state.counter);
    }
  }); 
})

See it here: http://codepen.io/borisd/pen/aOeWNX

Made you shudder?

Good.

Delete all the crap, thats not how we want to use ReactJS. While this method is fine for updating the Service data:

$rootScope.$apply=> PicsService.setCounter(100;

In ReactJS we prefer to pass *props* to Components.

Our *pic* directive’s template should look like this:

inside

Notice how we pass the counter data with counter=””

This puts the burden of $watch on our re-usable *react* directive.

What we will need is to get the *counter* parameter out, which is easy using attrs.counter and then to get the value hidden behind data.counter

We will need to turn our

React.render(React.createElement(component), element[0])

into

React.render(React.createElement(component, { counter: ? }), element[0])

Luckly scope.$watch is to the rescue again:

const component = $injector.get(attrs.react);
const render = (counter) => {
  React.render(React.createElement(component, { counter }), element[0]);
} 
       
scope.$watch(
  attrs.counter,
  render
);

First lets see that it’s really working by changing the *Pic* componet to render:

render() {
  return React.createElement('h2', {}, 
    "ReactJS is here! ", 
    this.props.counter);
}

Now it’s this.props.counter instead of this.state.counter (as props come from outside).

The code: http://codepen.io/borisd/pen/YXmVVy

Explanation

How did that work?

First we moved the rendering into a function that receives the counter:

const render = (counter) => {
  React.render(React.createElement(component, { counter }), element[0]);
}

Now calling render(100) will render the React component with this.props.counter set to 100.

Next we setup a watch on the directive’s *counter* attribute which triggers a new render(newVal) everytime the value of the expression changes.

Magic.

Final notes

Now that you have seen how easy it is to add ReactJS to the project; use a library that is already well tested and used like ngReact. The purpose of this tutorial was to make you understand how things work behind the scenes and avoid pitfalls in the future.

While ReactJS and AngularJS play well together, allways consider why and if you should combine them. If speed is the thing you are after, read ReactJS performance. If you want to use ReactJS because its architecture fits best in some of the components on your page (e. g. Complex DatePicker), then you might be pleasently suprised.

Special thanks to Shai Davis for proofreading the post.

The sample code is also availble in the github repo

PS

Don’t forget to clean your $watchers on desctruction – always.

AngularJS   React

Using Browserify To Enhance Your AngularJS Tests

Adam Klein

Why

  • Reusing code between Protractor and unit-tests
  • Using transpilers, like Babel, to allow ES6 code inside tests
  • Using node modules, like faker and factory-girl

Problem: Protractor & Jasmine require different API

Say you have defined a mock for your user object:

// user.mock.js
function UserMock() {  
  this.name = 'john doe';
  this.role = 'admin';
}

In order to use it in your unit tests, you would normally inject it as a factory on a mock module:

// user.mock.js
...
angular.module('mocks')  
  .factory('UserMock', UserMock);


// user.spec.js
...
var UserMock;  
beforeEach(module('mocks'));  
beforeEach(inject(function(_UserMock_) {  
  UserMock = _UserMock_;
});

Using the mock in Jasmine

In case you don’t understand how to share resources in Angular tests by using dependency injection, here is a short explanation:

  • Define a module (using angular.module)
  • Define a service / factory on it
  • Include the module using ngMock’s global module function (which is short for angular.mock.module)
  • Inject the service or factory that you want to use

Using the same mock in a Protractor test is much easier, but different.

Since Protractor tests run with Node.js and not in the browser, you have to define and use the mock as a Node module:

// user.mock.js
...
module.exports = UserMock;


// login.scenario.js
var UserMock = require('../mocks/user.mock.js');  
...

Using the mock in Protractor

Much simpler. Wouldn’t it be nice if we could use the same method for unit tests?

Solution

Using karma-browserify, we can define and use our mock using the node syntax both in our unit and Protractor tests.

First, install the plugin:

npm install karma-browserify --save-dev

Change your karma.conf.js as follows:

frameworks: ['jasmine', 'browserify'],
preprocessors: {
  '{specs,mocks}/**/**.browserify.js': ['browserify']
},
browserify: {
  debug: true
}

Then apply .browserify.js suffix to specs and mocks that use module.exports and require statements. The reason we do this is so that we precompile only these specs using browserify and not all specs and mocks.

Note: change the {specs, mock} to wherever your test files reside

The debug flag will help us debug the tests using source maps.

Now you can use the mock in your unit tests the same way you used it in Protractor:

// user.mock.browserify.js
...
module.exports = UserMock;


// user.spec.browserify.js
UserMock = require('../mocks/user.mock.js');  
...

Using the mock the same way

Using ES6

You can now easily use whatever transformers and plugins that Browserify supports. For example, for writing your tests using ES6 (including ES6 module system):

npm install --save-dev babelify

// karma.conf.js
  browserify: {
    transform: ['babelify']
  }

And then use ES6 inside your mocks / tests:

// user.mock.js
  class UserMock {
    ...
  }
  export default UserMock;

  // user.spec.js
  import UserMock from '../mocks/user.mock.js';

Using node modules in unit tests

There are Node modules that are very helpful for tests & mocks, such as faker. Using them in unit tests is easy, now that we have configured Browserify:

// user.mock.js
  var faker = require('faker');
  class UserMock() {
    constructor() {
      this.first_name = faker.name.firstName();
      this.role = 'admin';
    }
  }

Requiring Node modules from inside a test

Browserify is agnostic to the module system you’re using. You can use Node’s module.exports with ES6’s import, and vice versa — use ES6’s export with Node’s require:

// these work together:
  module.exports = UserMock;
  import UserMock from '../mocks/user.mock.js';

  // And so do these:
  export default UserMock;
  var UserMock = require('../mocks/user.mock.js');

  // It even works with node modules:
  import faker from 'faker';

Browserify is agnostic to the module system

Happy testing :)

AngularJS   Browserify   Testing

Is React Faster Than AngularJS?

Boris Dinkevich

No. Hard to believe?

Most developers and decision-makers take it for granted that ReactJS offers high performance and incredible speed much better than other frameworks like AngularJS and EmberJS.

It has gotten to the point that no one even questions things like this:

(Source)

But if you ask yourself where this belief comes from, you might be surprised.

Everyone says it

This doesn’t give us much to argue to with, so we won’t bother.

Virtual DOM

We all understand that DOM manipulation by the browser is slow.

This is where ReactJS came in and introduced the new idea of using a Virtual DOM. By calculating the difference between the future state and the current one, it can minimize the amount of DOM requests.

Intuitively, this sounds like a major performance improvement. But what about the performance impact caused by the massive amounts of JavaScript required to execute this complicated feat?

Or the strange lack of any demonstrable examples of the performance improvements achieved by this feature... except the comparison demos.

We have all seen the demos

This is perhaps the biggest culprit. So let us take a closer look at a few.

React.js Conf 2015 – Hype!

This is perhaps the most watched one. This presentation literally made the crowd go “wow”.

Here is the original demo:

Wow, right?

Let’s not take it at face value though. If we give it a closer look we find something very surprising. The demo’s author seems to neglect one of the most basic speed improvements that can be effected in Angular – “track by”.

Let’s fix that by changing a single line of code:

ng-repeat="(key, db) in databases"

to:

ng-repeat="(key, db) in databases track by key"

Try the result

Surprised? Sadly, AngularJS deserves some blame for that. The most common speed improvement is unfortunately badly documented and not auto-suggested by the framework.

This little change invalidates 95% of comparisons between ReactJS and AngularJS.

Next up: ng-conf: Angular + React

The next most popular presentation contains a similar “wow” moment.

Try it out

Here the issue is different. The comparison is not between rendering but rather between rendering ReactJS Components and rendering and data handling of AngularJS.

ReactJS is being told explicitly which cell changed while AngularJS is left with a generic “something changed” notification – forcing it to recheck everything.

Let’s level the playing field and give both frameworks the same information by using AngularJS’s isolated scope:

$timeout(function() {
  $scope.status.isSearching = false;
  $scope.status.searchResults = ...

Updated to:

setTimeout(function() {
  $scope.status.isSearching = false;
  $scope.status.searchResults = ...
  $scope.$digest();

The result? Try it out.

The above can be done on newer versions of angular by using $timeout([func], [timeout], false);

What does all this mean?

It appears that Virtual DOM-based frameworks (and, specifically, ReactJS) offer no demonstrable improvement over “plain” frameworks like AngularJS or EmberJS. The premise that adding ReactJS to AngularJS will magically improve performance is simply not based on factual data.

And while React itself offers a host of other improvements, I could not find any demonstration of the most commonly quoted advantage of speed.

Is React Bad?

No. React is a great framework which we at 500Tech use and love. There are many benefits to choosing ReactJS for your next project. “Speed” should not be one of them.

Notes

  • Any demonstrations that show speed boosts resulting from the Virtual DOM are most welcome.
  • Please do your AngularJS performance homework before sending them over.
  • Original presentation for ng-conf Israel
  • Since “fast” is a relative term, AngularJS was used as the comparison base. Both because it solves similar problems as ReactJS and both since it is usually used as the base comparison anyway.
  • While its easy to show that ReactJS is faster than AngularJS in certain cases, it is much harder to show that it is always slower. Over the next week we will release the various samples we wrote to test the speed differences.
  • Thanks Yoni Weisbrod for proof-reading the post.

Updates

26-May-2015:

  • People have sent me links to a number of other demos. All are simply missing ‘track by’.

In theory there is no difference between theory and practice. In practice there is.

Comments

Discussion on HackerNews

AngularJS   Industry   Performance   React
Earlier Ctrl + ↓