Uncategorized

Angular’s $parse for intelligent product suggestions

I’ve recently spent some time working on a prototype for a client who wanted us to implement some intelligence in a product suggestion process. For this purpose I was thinking of something I know from game development. As soon as it comes to AI (artificial intelligence) stuff you’re happy if you’ve implemented a DSL (domain specific language) into your game already. This then allows you to solve the AI problems with little DSL scripts that you can modify for each enemy. You can port your internal events like collisions, way point triggers and environmental changes into your DSL and build scripts that get used as strategies for your enemies to deal with them. A smarter enemy may also gets a smarter script.

Space invaders arcade game

But enough from games! Let’s talk about Angular.js and how to build a nice intelligent product suggestion for our digital product.

Building a whole DSL would probably be too much just for determining matching products. However, Angular.js already provides sort of a solution here. That’s right! Expressions! Angular.js expressions can contain function calls, evaluate variables, do boolean operations and execute filters. This would be a perfect substitution for our DSL.

We could now assign all properties we believe are relevant for the intelligent suggestion process to an object and execute a static rule.

var product = {
  name: 'Cool thingy',
  price: 100,
  coolness: 80
};

Using such objects in a list and with a simple ngRepeat and ngIf directive we can filter out products that don’t match the current user wishes.

<article 
  class="product"
  ng-repeat="p in products"
  ng-if="p.price < 300 && p.coolness > 50">
  {{p.name}}
</article>

But is that going to be sufficient and flexible enough to call it an intelligent product suggestion? I don’t believe so. Let’s see how we can make this better by using Angular’s $parse service.

Angular.js has an internal service called $parse. It’s basically the expression parser of Angular and wherever it needs to parse expressions it’s using this service. Of course this service can also be used outside of Angular’s liver, pancreas and heart and brings a lot of cool features to your own code.

$parse converts Angular expression into a function.

Using $parse is very simple. Just inject it as a dependency in your controller function and use it to parse expressions.

angular.module('myApp', [])

  .controller('MyCtrl', function($scope, $parse) {
    // Using $parse to turn an expression into a function
    var expression = $parse('100 > 99');
    // Calling the function will evaluate and return
    $scope.evaluated = expression();
  });

A expression turned into a function by $parse accepts a context object as a first parameter. Properties of this context object get directly exposed as variables inside of the expression. Expressions are isolated by nature and parsed (not like their old evil brother eval() ) and they need a context as input.

angular.module('myApp', [])

  .controller('MyCtrl', function($scope, $parse) {
    // We will parse an expression containing variables
    var expression = $parse('a > b');
    // Now we parse the expression with a given context
    $scope.evaluated = expression({
      a: 100,
      b: 99
    });
  });

So how can we use $parse in our product example above? We can even do a favor to our code and use a great design principle which is inversion of control. Instead of filtering specific properties of our product from outside the product itself we can set up a rule inside of the product and evaluate it from outside. This would give us all freedom we need in the product itself in order to determine if it matches or not. We can use the context object to pass in environmental data that will be needed in the product match expression. This includes user input like price ranges, availability and other factors.

angular.module('myApp', [])
 
  .controller('MyCtrl', function($scope, $parse) {
    // Define some environmental parameters that can
    // be changed by the user
    $scope.env = {
      age: 29,
      sex: 'male'
    };
    
    // Define two products with a rule to match them
    $scope.movies = [
      {
        name: 'Terminator',
        rule: 'age > 16 && sex === "male"'
      },
      {
        name: 'Forest Gump',
        rule: 'age > 10'
      }
    ];
    

    // Now we parse the expression with a given context
    $scope.filteredMovies = $scope.movies.filter(function (movie) {
      // We will parse the expression of the movie
      var expression = $parse(movie.rule);
      // An return the boolean expression result for filtering
      // by passing the env as context
      return expression($scope.env);
    });
  });

I’ve created a working example of this on JSBin where you can see a simple but intelligent product suggestion in action. It’s a simple App that suggests you food based on input. Don’t mind cloning it and mess around. In the example you can also see that you can use filters to simplify your expressions. With the help of filters you can easily build more complex DSL like functionality into your rule expressions. There is really no limit except our imagination.

Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s