Check out our new training course on AngularJS for Flex Developers

Comparing AngularJS Providers, Factories, Services, Constants, Values, and Decorators

This post is is designed as a reference cheat sheet. I've written about Providers, Factories, Services, Constants, Values, and Decorators in more detail in past posts. This is a simple table intended to compare them. I had something similar in the bonus materials of my AngularJS training course; but it didn't include Constants, Values, or Decorators.

Element Singleton Instantiable Configurable Comment
Provider Yes Yes Yes
  • A Configurable Factory.
  • The code behind both the Service, Factory, Value, Constant, and Decorator.
  • Available during the configuration phase.
  • Can be intercepted by decorator.
Factory Yes Yes No
  • A Injectable function.
  • Returns an Object
  • Easy to pass arguments to a constructor since you create your own object.
  • Can be intercepted by decorator.
Service Yes No No
  • A Simple Injectable Constructor.
  • Uses the new keyword to create an object.
  • Returns an instance of the object.
  • Can be intercepted by decorator.
Value Yes No No
  • A Simple injectible value.
  • Can not be injected into config blocks.
  • Can be intercepted by decorator.
Constant Yes No No
  • Cannot be intercepted by a decorator.
  • It is possible to change this value programmatically.
Decorator Yes No No
  • Used to modify other elements in this list, except constants.
  • Can be intercepted by another decorator.

Sign up for DotComIt's Monthly Technical Newsletter

Using Decorators in AngularJS

A decorator is a design pattern. A decorator is, basically, a wrapper for an object. It doesn't change the API of that object, but does change the functionality.

One use for decorators might be to add authentication code to a services API. The authentication code could be encapsulated and use for all service calls; but doesn't inherently change the functionality of a single method. Decorators can be used for caching purposes in a similar manner.

There is a decorator method implemented as part of the AngularJS library. It allows you to use a decorator to modify an Angular service, factory, provider, or value. This feature was added in Angular 1.4, before I wrote my AngularJS training course. This article will tell you how to use a decorator in the context of an AngularJS Application.

Creating a Simple Angular Application

The first step in any AngularJS application is to import the angular framework:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create a module:


angular.module('decoratorTest',[]);

The module should be put in a script block, in case that wasn't obvious.

Next, create a service:


angular.module('decoratorTest').service('myService', function(){
return {
value : "Value in Service"
}
});

The service is named myService; and the service object contains a single property, named value.

Next, create a controller. The controller will add the myService to the $scope so the value can be displayed within a view:


angular.module('decoratorTest').controller('controller1',['$scope','myService',
function($scope,myService){
$scope.myService = myService;
}]);

The controller's name is controller1. It has two services injected into it: Angular's $scope service and our custom myService. Create a view to output the data:


<body ng-app="decoratorTest">
<div ng-controller="controller1">
<b>Service in Controller 1</b>: {{myService.value}}
</div>
</body>

The decoratorTest module is added to the body tag using the ngApp directive. The controller1 controller is put on a div with the ngController tag. The service value is output. You should see something like this:

This a simple app that simply displays some data from the controller in the view. It does not yet use a decorator.

Using a Decorator

Now let's add a decorator to the app. This decorator will change the value constant instead the service. AngularJS defines a decorator using a method off the main module. This is the generic format:


AngularJSModule.decorator(ServiceTobeModified, ['$delegate',function($delegate){
return {
// modified service
}
}

The decorator function is called on the module. It accepts two arguments. The first argument is a string that refers to the service, or factory, or provider, or value to be modified. The second argument is the modifier function. In this case, we are injecting a single item into the decorator and that is the service function to be modified. The $delegate argument is injected to the decorator function using standard AngularJS dependency injection syntax. It represents the service as is, without any modifications.

For the purposes of this sample, we are going to modify the value on the service to say 'decorated' after the original value. This would be our modified code:


angular.module('decoratorTest').decorator('myService',['$delegate', function($delegate){
return {
value : $delegate.value + " decorated"
}
}]);

Run your updated app and you should see something like this:

Play with the code

Final Thoughts

Technically we could create decorator like functionality using providers and an Angular config. But, using the decorator function is a much easier approach. It also allows us to add decorators to our app at any point, not just at the config stage. Decorators can be used for much more advanced purposes than this sample. We could add new functionality or values to our service.

I find decorators are most useful when dealing with third party services that I want to use almost as is, but make minimal changes. Have you been using them? What for?

Sign up for DotComIt's Monthly Technical Newsletter

Using AngularJS Factories

A Factory is the name for a design pattern in which one object is used to create other objects. One of the AngularJS Recipes is named the Factory recipe. For practical purposes, a factory in AngularJS is not much different than a service in AngularJS. They are both used to share data between controllers.

This article will demonstrate how to use a factory within an AngularJS application.

Creating a Factory in AngularJS

Let's create a simple app that creates an Angular service and uses it to share data between two separate controllers. First, load the angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create an angular module:


angular.module('factoryTest',[]);

The next step is to create the value. This is done using the service function on the angular module:


angular.module('factoryTest').factory('myFactory', function(){
return {
value : "Value in Factory"
};
});

The factory method accepts two values. The first is the name of the factory, which will be referenced by Angular when injecting the factory into a controller. The second argument is the function which returns the factory object. The factory object is a standard JavaScript object and can contain properties, variables, and functions. In this case the factory is named myFactory and the factory object contains a single simple property, named value. In AngularJS a factory creates a Singleton object, meaning only a single version of this object will be created. Every time it is referenced the same object will be returned.

Let's create a couple of controllers that use the service:


angular.module('factoryTest').controller('controller1',['$scope','myFactory',
function($scope,myFactory){
$scope.myFactory = myFactory;
}]);
angular.module('factoryTest').controller('controller2',['$scope','myFactory',
function($scope,myFactory){
$scope.myFactory = myFactory;
}]);

The controller function uses two arguments. The $scope is a built in Angular service used for sharing data between a view and controller. The myFactory value is a reference to custom factory created for this sample. Inside the controller, the factory object is copied into the $scope so the data can manipulate within the view. The two controllers are almost identical.

Let's take a look at the views:


<body ng-app="factoryTest">
<div ng-controller="controller1">
<b>Factory from Controller 1</b>: {{myFactory.value}}
</div>
<div ng-controller="controller2">
<b>Factory from Controller 2</b>: {{myFactory.value}}
</div>
</body>

The Angular Module is placed on the body tag using the ngApp directive. Each controller is attached to a div with the ngController directive. Inside each view; the factories value is displayed. Run the app and you should see something like this:

Try out this sample here.

What happens when we change the value?

Angular Factories operate just like Angular Services under the hood. When you change a value on the factory, it is automatically updated elsewhere. Let's add some input that allows us to change the value:


<div ng-controller="controller1">
<input type="text" ng-model="myFactory.value"/><br/>
<b>Factory from Controller 1</b>: {{myFactory.value}}
</div>
<div ng-controller="controller2">
<input type="text" ng-model="myFactory.value"/><br/>
<b>Factory from Controller 2</b>: {{myFactory.value}}
</div>

Rerun the app and type in either of the checkboxes. You'll see changes are automatically updated between both views:

Play with this app here

What Happens if I Copy a Factory's Object Properties into the $scope?

There is a mistake I made too many times admit; so I wanted to write about it here. When you copy the factory into the $scope, as we did previously, Angular binding automatically updates values. But, if you copy a factory's property directly into the $scope; the value is not updated. Let's put together a sample demonstrating a situation where Factory properties are not bound and automatically updated.

First modify the two controllers:


angular.module('factoryTest').controller('controller1',['$scope','myFactory',
function($scope,myFactory){
$scope.factoryValue = myFactory.value;
}]);
angular.module('factoryTest').controller('controller2',['$scope','myFactory',
function($scope,myFactory){
$scope.factoryValue = myFactory.value;
}]);

Previously, we copied the full factory object into the $scope. In this case only the value on the object is copied. Modify the views to reference the new $scope value:


<div ng-controller="controller1">
<input type="text" ng-model="factoryValue"/><br/>
<b>Factory from Controller 1</b>: {{factoryValue}}
</div>
<div ng-controller="controller2">
<input type="text" ng-model="factoryValue"/><br/>
<b>Factory from Controller 2</b>: {{factoryValue}}
</div>

Now test it out:

When the value is changed within the first controller's view it is not updated in the second controller. Simple values are not updated, but object values are.

Play with this sample here

Final Thoughts

Under the hood; an AngularJS Factory is one layer of abstraction removed from an Angular Service. The method for creating a service, actually calls the factory method. A service will create a new instance of the object using the new keyword while a factory does not use the new keyword when creating the factory object. For practical purposes, when building apps, this detail probably doesn't matter.

Sign up for DotComIt's Monthly Technical Newsletter

Using AngularJS Services

The most common approach I use for sharing data between two AngularJS controllers is a service. An AngularJS service is a UI mechanism, and for the purposes of this article I am not referring to remote REST services that populate your application with data. I am talking about facilities that AngularJS provides to share data between different views and controllers.

This article will demonstrate how to use a service within an AngularJS application.

Creating a Service in AngularJS

Let's create a simple app that creates an Angular service and uses it to share data between two separate controllers. First, load the angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create an angular module:


angular.module('serviceTest',[]);

The next step is to create the value. This is done using the service function on the angular module:


angular.module('serviceTest').service('myService', function(){
return {
value : "Service Value"
}
});;

The service method accepts two values. The first is the name of the service and the second is a function which returns the service object. The service object is just like any JavaScript object and can contain functions or properties or variables. In this case the service is named myService and the service object contains a single property, named value. In AngularJS a service is a Singleton object, meaning only a single version of this object will be created.

Let's create a couple of controllers that use the service:


angular.module('serviceTest').controller('controller1',['$scope','myService',
function($scope,myService){
$scope.myService = myService;
}]);
angular.module('serviceTest').controller('controller2',['$scope','myService',
function($scope,myService){
$scope.myService = myService;
}]);

The controller function accepts two separate arguments, the $scope and the myService. The $scope is a built in Angular Service that helps share data between the view and controller. The myService is our custom service which helps share data between our two controllers. The service object is copied into the $scope so we can manipulate its' properties inside the view. The two controllers are almost identical.

Let's take a look at the views:


<body ng-app="serviceTest">
<div ng-controller="controller1">
<b>Service in Controller 1</b>: {{myService.value}}
</div>
<div ng-controller="controller2">
<b>Service in Controller 2</b>: {{myService.value}}
</div>
</body>

The Angular Module is put on the body tag with the ngApp directive. Each controller is attached to a div. Currently the thing the controller does is display the myService's value property. Run the app and you should see something like this:

Try out this sample here.

What happens when we change the value?

A big benefit to Angular services is that when you change a service value, it is automatically updated elsewhere. Let's add some input that allows us to change the value:


<div ng-controller="controller1">
<input type="text" ng-model="myService.value"/><br/>
<b>Service in Controller 1</b>: {{myService.value}}
</div>
<div ng-controller="controller2">
<input type="text" ng-model="myService.value"/><br/>
<b>Service in Controller 2</b>: {{myService.value}}
</div>

Rerun the app and give it a shot:

Play with this app here.

What Happens if I Copy Service Object Properties into the $scope?

When writing up this article, I Wondered what would happen if we copied the service value into the $scope instead of the whole service. Would values still change? Let's find out!

First modify the two controllers:


angular.module('serviceTest').controller('controller1',['$scope','myService',
function($scope,myService){
$scope.myServiceValue = myService.value;
}]);
angular.module('serviceTest').controller('controller2',['$scope','myService',
function($scope,myService){
$scope.myServiceValue = myService.value;
}]);

Instead of the service object being copied into the $scope; now the value on the object is copied to the scope. Modify the views to reference the new $scope value:


<div ng-controller="controller1">
<input type="text" ng-model="myServiceValue"/><br/>
<b>Service Value in Controller 1</b>: {{myServiceValue}}
</div>
<div ng-controller="controller2">
<input type="text" ng-model="myServiceValue"/><br/>
<b>Service Value in Controller 2</b>: {{myServiceValue}}
</div>

Now test it out:

When the value is changed within a controller's view it is not updated to the other controller. I ran into similar functionality when dealing with values and constants. Simple values are not updated, but object values are.

Play with this sample here

Final Thoughts

Angular Services are my goto mechanism for sharing data within Angular applications. They seem the simplest to use and implement inside an application and are very flexible.

Sign up for DotComIt's Monthly Technical Newsletter

How do I specify a startIndex when using AngularJS ngRepeat ?

TLDR: use a filter.

I was working on an app for a client and wanted to create a drop down from a controlled vocabulary. In this particular case I did not want to show the first element of the array which would populate the drop down. In an ideal world, I would just have the service guys give me a service that omitted the unnecessary elements for UI use. In this case that wasn't an option. I needed to do something to remove the elements in the UI.

I could have written code to loop over the array, and create a new array. But, instead I decided to use an AngularJS Filter.

The Problem

Let's piece together an app to demonstrate this:

First, import the Angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create an AngularJS Application:


angular.module('testModule',[]);

This code should go in another Script block.

Then create a controller:


angular.module('testModule').controller('testController', ['$scope',function($scope){
$scope.someControlledVocabulary = [
{id:0,label:'Don\'t Show This One'},
{id:1,label:'Value 1'},
{id:2,label:'Value 2'},
];

$scope.selectedValue = $scope.someControlledVocabulary[1].label;

}]);

The controller contains a hard coded controlled vocabulary. The $scope also contains a selectedValue that refers to the second item in the controlled vocabulary. You could default this to anything.

Now, create the view, with a select box:


<body ng-app="testModule">

<div ng-controller="testController">

<b>Drop down with no filter</b>:
<select ng-model="selectedValue">
<option ng-repeat="vo in someControlledVocabulary" value="{{vo.label}}" >{{vo.label}}</option>
</select>
<br/><br/>
<b>Selected Value</b>: {{selectedValue}}<br/><br/><br/>
</div>
</body>

Run the code, and you should see something like this:

You'll see that all the items in the controlled vocabulary are displaying in the drop down. We want to prevent that.

Create a Filter

In your JavaScript code block, create a filter. I wrote a bit about filters in the past. This is our new filter:


angular.module('testModule').filter('startAt', function(){
return function(inputArray, startAt) {
return inputArray.slice(startAt);;
};
});

The AngularJS filter is named startAt. It's value is a function that returns the filter function. The inputs are the item being filtered--in our case the controlled vocabulary array--and the item index you want to start your display at. The actual filter function uses JavaScript's slice method to create a new array with all items after the startAt value.

Modify the view where the ng-repeat loops over the options to add the Filter:


<option ng-repeat="vo in someControlledVocabulary | startAt:1" value="{{vo.label}}" >{{vo.label}}</option>

When the view is processed the code will truncate the controlledVocabulary and the first item in the drop down will be skipped:

Everything works great. You can modify the startAt value to remove more than one item from the drop down.

Play with this code on Plunkr.

Like This? Sign up for my Monthly Newsletter

Why is AngularJS removing spaces on my inputs?

An app that I'm working on needed to validate a user's text input to make sure that they did not put spaces before, or after, the actual text. The business decision was made to warn the user of the extra spaces instead of automatically trimming the data. However, when it came time to write the validation code; we found that AngularJS was automatically trimming spaces. What can we do about that?

The problem

First, let's build an app to demonstrate the problem. Load in the Angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create an Angular Module in another script block:


angular.module('trimTest',[]);

Next, create an Angular controller:


angular.module('trimTest').controller('someController',['$scope', function($scope){
$scope.value1 = " Hello ";
$scope.onValue1Change = function(){
console.log($scope.value1);
}
}]);

This controller keeps a single value in the $scope and has a single function that will execute when the view input is changed.

This is the view code:


<body ng-app="trimTest">
<div ng-controller="someController">
<input type="text" ng-model="value1" ng-change="onValue1Change()"> <Br/>
<pre>
{{value1}}!
</pre>
</div>

An HTML text input is used and the ng-model ties associates the input with the scope value, value 1. The value is output inside a pre tag; so that all spaces are shown and not truncated.

Try this app. Type in text box and you'll see that the display inside the pre doesn't change no matter how many spaces you type. Watch the console debug output for the onValue1Change() method and you'll see that typing spaces, either before, or after the text in the input does not trigger the change value.

Angular is automatically trimming spaces before performing a compare which will trigger the ngChange. I've had similar experiences when putting my validation code inside a directive. The problem is that if a user adds extra spaces before or after their text, my validation code will not execute, and therefore the user will not get feedback of their erroneous input in the UI.

The Solution

The Angular developers have planned for my problem. They added an Angular attribute to the input tagged named ngTrim. To demonstrate, we'll modify our previous application, so we can see an input with an ngTrim side by side to one without. First, add anew $scope variable inside the controller:


$scope.value2 = "World";

Also add a change function for the new input:


$scope.onValue2Change = function(){
console.log($scope.value1);
console.log($scope.value2);
}

This change function outputs both value1 and value2 from the $scope. For completeness, Modify the onValue1Change() function to do the same:


$scope.onValue1Change = function(){
console.log($scope.value1);
console.log($scope.value2);
};

Now modify the view code:


<b>Value with ngTrim not set</b>:
<input type="text" ng-model="value1" ng-change="onValue1Change()"> <br/>
<b>Value with ngTrim set to false</b>:
<input type="text" ng-model="value2" ng-trim="false" ng-change="onValue2Change()"><br/><br/>
<pre>
{{value1}}! {{value2}}!
</pre>

I added some labels to each input. The second input uses ngModel to associate itself with the value2 in the controller's $scope. The ngTrim attribute it set to true; and the ngChange will call the onValue2Change() function.

Play with this code . The second value, with the ng-trim set to false, will add spaces before or after the text as they are typed. This is immediately referenced in the view display, and you can also see it in the console output from the ngChange method.

Sign up for DotComIt's Monthly Technical Newsletter

Using Values in AngularJS

AngularJS has something they call the value recipe. Values are similar to constants, something I already wrote about. The difference between an AngularJS constant and an AngularJS value is very subtle. Constants and values are the same with the exception that a value can be injected into a configuration block and then changed before it is injected into controllers or services. Constants cannot be injected into configuration blocks.

This article will demonstrate how to use values within an AngularJS application.

Creating a Value in AngularJS

Let's create a simple app that creates an Angular value and shares it between two separate controllers. First, load the angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Then, create an angular module:


angular.module('valueTest',[]);

The next step is to create the value. This is done using the value function on the angular module:


angular.module('valueTest').value('someStringValue','someStringValue');

The value accepts two different values. The first is the name of the value and the second is the value of the value. That sounds more confusing than it should. The syntax is similar to what you would use when creating a service, or controller, or constant. Let's create a controller that makes use of the value:


angular.module('valueTest').controller('someController',['$scope','someStringValue', function($scope,someStringValue){
$scope.someStringValue =someStringValue;
}]);

The someStringValue value is passed into controller using angular's dependency injection syntax. Then the value is copied into the $scope so it can be used in the view code. The second controller uses a similar approach:


angular.module('valueTest').controller('someController2',['$scope','someStringValue', function($scope,someStringValue){
$scope.someStringValue =someStringValue;
}]);

Let's take a look at the views:


<body ng-app="valueTest">
<div ng-controller="someController">
<b>Controller1 value</b>:{{someStringValue}}
</div>
<div ng-controller="someController2">
<b>Controller2 value</b>: {{someStringValue}}
</div>

This sample mirrors the sample I used in my article about constants. Run the app you should see something like this:

Check out the sample here.

What happens when we change the value?

When I first read about values in AngularJS I was given the impression that the difference between a value and a constant was that a value could be changed while a constant could not be changed. So, when creating this article I wanted to see what would happen if we changed the value. We can modify the view to add an input that allows us to change the value:


<div ng-controller="someController">
<b>Controller1 value</b>:{{someStringValue}}<br/>
<b>Value</b>:
<input type="text" ng-model="someStringValue" size="50"/>
</div>
<div ng-controller="someController2">
<b>Controller2 value</b>: {{someStringValue}}<br/>
<b>Value</b>:
<input type="text" ng-model="someStringValue" size="50"/>
</div>

Rerun the app and give it a shot:

Play with this app here.

The results surprised me. Changing the value inside one controller did not affect the other controller. The value is treated exactly like a constant is in this situation. There is no automatic binding or updates of the value between two controllers.

What about Object Properties?

When I wrote about constants, I wondered if the constant changes would trickle through controllers if the constant was an object instead of a value. I found that this was the case. I did the same experiment with values and found the same results.

First, create the value:


angular.module('valueTest').value('someObjectValue',{value:'someObjectValue'});

Then modify the two controllers to reference someObject value instead of someStringValue:


angular.module('valueTest').controller('someController',['$scope','someObjectValue', function($scope,someObjectValue){
$scope.someObjectValue=someObjectValue;
}]);
angular.module('valueTest').controller('someController2',['$scope','someObjectValue', function($scope,someObjectValue){
$scope.someObjectValue =someObjectValue;
}]);

Then modify the views:


<div ng-controller="someController">
<b>Controller1 value</b>:{{someObjectValue.value}}<br/>
<b>Value</b>:
<input type="text" ng-model="someObjectValue.value" size="50"/>
</div>
<div ng-controller="someController2">
<b>Controller2 value</b>: {{someObjectValue.value}}<br/>
<b>Value</b>:
<input type="text" ng-model="someObjectValue.value" size="50"/>
</div>

Test out the app:

Play with this here.

When the object's value property is modified in one controller, the change immediately propagates to the other controller. This is because the value is a link to the object; and changing properties on the object do not change the actual value.

Final Thoughts

In practical terms; Angular values are not much different than Angular constants. The real difference is that the value can be injected into an AngularJS configuration block while the constant cannot be. In the configuration block, the value could be changed before the value is injected into services or other controllers. The constant, on the other hand, cannot be injected into a configuration block; and as such cannot be changed before being injected into a service or controller.

Honestly, to me, I'm failing to see the need for constants and values. Most of the time I find myself wrapping shared values inside Angular services. Angular services are designed to be shared between controllers and when values change Angular binding handles it for us.

Tell me what I'm missing! How do you use Angular values in your application development?

Sign up for DotComIt's Monthly Technical Newsletter

How to use indexOf() function in JavaScript

I have been working on an AngularJS project for a client. During a code review I came across code that uses $.inArray(). I flagged the line saying that I prefer not to use JQuery code within an Angular controller. I also stated that a more Angular way was to use forEach() or even a vanilla JavaScript for loop.

My team member pointed out that this code wasn't processing all items in the array; just looking for one. Out of curiosity; I opened up the JQuery framework and sure enough; inArray() is implemented as a for loop in the JQuery framework. Sometimes you have to pick your battles with clients, so I let this one slide.

My second impulse was to recommend they use indexOf(). This article will talk about what indexOf() is and how it can be used.

What is indexOf()?

The indexOf() function takes the format of this:


valueToSearchIn.indexOf(valueToSearchFor, startLocation);

The important pieces are:

  • valueToSearchIn: The valueToSearchIn variable is a string or array and is the element you want to search within. You call the indexOf() function on this variable.
  • valueToSearchFor: This valueToSearchFor parameter is the first parameter to the indexOf() function. It contains the value you want to find in the valueToSearchIn element.
  • startLocation: the startLocation refers to the index you want to start searching. If left out, it is set to 0 which is the first element of your string or array.

The return value from the function will be an integer referring to the location of the first instance of the valueToSearchFor. If -1 is returned then no value was found.

Using indexOf() with a String

Let me show some examples. First, create a JavaScript string:


var str = 'This is a String';

Now, try this:


console.log(str.indexOf('i'));

What should the return value be? It should be two because if we traverse the original string, you'll see:

0 = T

1 = h

2 = i

The method finds the first instance of the lowercase i and returns true. Here are a few more samples:


console.log(str.indexOf('This')); // returns 0
console.log(str.indexOf('is')); // returns 2
console.log(str.indexOf('str')); // returns -1 because this search is case sensitive
console.log(str.indexOf('Str')); // returns 10

Using indexOf() with an Array of Simple Values

Next create a string array:


var stringArray = ['This','is','a','String','Array'];

We can test a few items:


console.log(stringArray.indexOf('i'));

The result of this will be -1 because no string value consisting of a lowercase I exists within the array. When using indexOf on an array; the two values must match identically. Even though item 0 ('This'), item 1 ('is'), and item 3 ('String') contain a lowercase I they will still not equal to the lowercase I and will return nothing.

Here are a few more comparisons:


console.log(stringArray.indexOf('This')); // returns 0
console.log(stringArray.indexOf('is')); // returns 1
console.log(stringArray.indexOf('str')); // returns -1
console.log(stringArray.indexOf('Str')); // returns -1
console.log(stringArray.indexOf('Array')); // returns 4

Using indexof with an Array of Objects

Let's see how this function works with an array of objects. First, create the object array value:


var objectArray = [{value:'This'},{value:'is'},{value:'a'},{value:'Object'},{value:'Array'}];

Now, try to look for the string with a lowercase 'I':


console.log(objectArray .indexOf('i'));

This will return -1; because the literal string containing a lowercase I will not be equal to any of the objects in the objectArray.

Now try this:


console.log(objectArray .indexOf({value:'is'}));

What do you think will happen? Even though the object we are searching for is the functional equivalent of an object in the objectArray, the result will still be -1. The indexOf() test is checking whether two objects are the same; not comparing properties inside an object. Even though the two objects seem similar to us, visually, they are not holding the same place in memory and therefore are not the same object according to the browser.

Try the compare with an object directly out of the array:


console.log(objectArray .indexOf(objectArray[3]));

This will return 3; because we checking for the third object in an array.

Try this:


var tempObject = objectArray[4];
console.log(objectArray.indexOf(tempObject));

This copies an object from the array into a local variable. When the indexOf() function searches for that object in the array it finds it, properly, and returns the index value of 4.

You can view all the samples at this plunker

Why does JQuery include a custom implementation of indexOf?

JQuery includes its own implementation of indexOf(), and that implementation is used by the inArray() method. According to the documentation, JQuery has its own implementation for performance reasons; and gives this link as proof. I ran the tests in Firefox, Edge, and Chrome. Primarily the custom indexOf() function was quicker than the native one. For the low index test; Chrome did report that the native indexOf() performed better than the custom indexOf() function. I don't perceive the differences would amount to anything calculable in a real world application--unless you are going to performing the indexOf() function in a loop.

My preference would be to use the native indexOf() function; because I think that would improve readability of the code. What do you think? Is that impulse misplaced? Should I focus more on tweaking every millisecond of performance about of JavaScript?

Sign up for DotComIt's Monthly Technical Newsletter

My Road to a SurfaceBook

You probably know Microsoft introduced the Surface Book on October 6th. It is a well designed, and powerful, Windows laptop. My current laptop has developed an annoying hum so a getting a new one seemed to make sense. I decided to splurge and on October 7th and placed a pre-order. This is the formal complaint letter I filed with Microsoft's Executive Escalation team; documenting my horrible pre-order experience.

My Formal Complaint

October 29th, 2015:

My name is Jeffry Houser and I wish to file a formal complaint about Microsoft's handling of my Surface Book pre-order.

As you know, Microsoft announced the Surface Book on October 6th. I was very excited about getting a powerful well engineered Windows Laptop and decided I had to have one.

What follows contains the full details of my pre-order experience problems including order number, case numbers, and the names of support people I spoke with. I hope you'll excuse if me I have incorrect names on the list; as my notes are imperfect, and my phone connection to the support personnel was never crystal clear.

On October 7th: I placed a pre-order for an i7 512GB with 16GB of RAM and a Surface Dock.

On October 19th: Microsoft tried to charge my American Express card and American Express triggered a fraud warning, declining the transaction. I got a phone call from American Express and immediately approved the charge. American Express requested I have Microsoft do a re-authorization on the card and they would approve it. I also received an email from the Microsoft Store asking me to update my payment information.

I tried to re-run the charge on the Microsoft Store site and it was declined. I was notified I only had three remaining chances to fix this issue.

I tried to enter my credit card details as a new card and it was declined because the card was already on file. I tried to re-run the charge on the Microsoft store site a second time with the existing credit card profile and it was declined. I was notified I only had 2 remaining chances to fix this issue.

I called Microsoft around 3pm and spoke to "Roan" who told me a lot of people were having the same problem. Microsoft was working on it. I should wait a few hours to try again.

Around 6pm I tried to run the charge on the Microsoft site a third time and it failed, letting me know I only had a single opportunity to fix the issue remaining.

Around 6:30pm I got on the phone with American Express. They informed me that no additional charge attempts were made against my card and they did not reject it. With American Express on the phone, insisting the charge should go through, I submitted my last attempt through the Microsoft Store and the order was cancelled. Before getting off the phone with American Express, I had them verify that no charge was attempted or declined on their end.

After getting off the phone with American Express, I called the Microsoft Store immediately. I spoke to "Christie". She assured me that the order was not cancelled and that it was just a problem with the Microsoft Store system and the cancellation email was erroneous. She promised me a call back within 24 hours.

On October 20th: I did not get a phone call back within the 24 hour time frame. I called the Microsoft Store around 9:30pm. I was told they were still waiting for an update from a high level support team and promised a call back in 24-48 hours.

Around 10:30pm I received a call from Gloria from Microsoft Store Support. She created a new order for me and promised that my original ship date of October 26th would be honored.

On October 21st: I noticed that the ship date on the second order for the Surface Book listed 6-7 weeks which is very different than what I was promised verbally. I called Microsoft Store and was assured that the email was erroneous and I would still receive the product on the 26th.

On October 22nd: Microsoft called me to cancel the 2nd order and place a new one. I spoke to two support technicians in two separate calls. Both promised I would get the product by the original promised ship date. The Surface Dock on the second order was kept active while the Surface Book on the second order was cancelled.

In retrospect, I am unclear why this was required.

On October 26th: I called Microsoft Store Support around 8:00am to get a tracking number for the Surface Book. The Microsoft Store could give me no information. My Surface Dock was received on this day, but there is no sign of my Surface Book.

On October 27th: I received an email to let me know there was no update on shipping dates.

On October 28th: I received an email to apologize for the delay.

On October 29th: I called Microsoft. This time I called the number in the October 28th email (1-800-642-7676) instead of the phone number in the Order Emails (1-877-696-7786). On the first call, I got disconnected immediately. I assume this was an honest mistake.

I called the number again spoke to someone who asked a lot of my information. In my frustration, I used some inappropriate language. I should not have let my emotions get the better of me, a feel remorse for my verbal slip. I was transferred to the Microsoft Store support team.

I spoke to "Kemp". He said there was a chance my order may get delivered up today, or that it may show up in 6-7 weeks. I pushed for a specific delivery date and asked to speak to a supervisor. He put me on hold and eventually came back to tell me Monday November 9th. I asked once again to speak to a supervisor. After being put on hold, Eugene came on the line. Eugene offered me a $50 discount off my order and promised me a delivery date of November 2nd. During this single call I was given 4 different delivery windows. Given past delivery promises, why would I have any faith that any of them are correct?

At this point, I decided it was time to submit a formal complaint. I've had six different support reps promise me that my order will ship by the 26th; yet here it is four days past that and I'm getting a different story every time I talk to someone.

You can watch a full 30 minute edit of the support calls from before October 26th. You can also watch, a shorter compilation of your Microsoft Store Reps promising me the original ship date will be honored.

Your Microsoft Store staff has always been polite and professional, but ineffective.

This experience has soured my enthusiasm for the Surface Book. As long-time Microsoft Customer, and a small business owner who makes much of his living developing software with Microsoft technologies, I am left with a sour taste in my mouth. I'm no longer asking myself "When will the Surface Book ship?" I've started asking myself "What can Microsoft due to keep me as a customer?"

Can you help restore my faith in Microsoft?

The Aftermath

10/29/2015: Justin H from Microsoft Global Escalation Services emails me in response to my request to file a complaint and tells me he sees nothing wrong with the order. This was in response to me calling Redmond and asking to file a formal complaint, but before I actually could file the formal complaint. The letter above was sent shortly thereafter.

10/30/2015: I walk into a Microsoft Store and buy a SurfaceBook i7 off the shelf. They had a pre-order that wasn't picked up. Now; I have the machine part by coincidence and part by luck.

10/30/2015: Justin H emails me to tell me that the i7s are not shipping until 11-20 and the dates I was given were wrong all around. It turns out either Justin or his warehouse fulfillment team is confusing the Surface Pro 4 i7 with the SurfaceBook i7. I'm not sure if this mixup was an accident or something done just to try to satiate an upset customer.

I email back to have Justin cancel my existing order. He also offers me a partial refund of--roughly--10% of the purchase price. I accept.

10/31/2015: I speak to Justin H on the phone and he apologize for all the problems and promises me the refund. He is going to close the ticket I opened with him. IF there are any problems with the refund or the computer, I should email back and it will reopen the ticket.

So, here we go:

I'm fairly happy with the unit and hope to write a formal review at some future point. I don't think I'll ever be pre-ordering anything from the Microsoft Store web site again.

Why are my AngularJS Directive’s attributes undefined?

Almost every time I create an AngularJS directive, I have trouble passing in values to it. I'm writing this article to remind me of my mistakes. Perhaps you are running into the same issues. If so, I hope this helps.

Create a Directive with Problem 1: Case Sensitivity

First, let's create the directive:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>
<script>
angular.module('directiveApp',[]).directive(dciTestdirectivecs,function(){
return {
restrict: 'AE',
template : "<b>Some Value</b>: {{someValue}}<br/><br/>",
scope : {
someArg : "="
},
controller : function($scope){
$scope.someValue = $scope.someArg;
}
}
});
</script>

First, load in the angular framework. Then create a module specifically for the directive. I named this module directiveApp. The directive is created as part of the module, named dciTestdirectivecs. I defined the module's template in-line. The sole purpose of this directive is to display the value that we pass into it. A scope is defined to give the directive an isolated scope. A single argument is passed into it--someArg.

The directive's controller saves the $scope argument, someArg, into the $scope value someValue. The $scope argument, someValue is output as part of the template.

Let's write some more code to turn this into a runnable application.

First, add a module as part of the script tag:


angular.module('directiveArgumentTest',['directiveApp'])

Then in the main HTML of your page, attach the module:


<body ng-app="directiveArgumentTest">

And use the directive:


<dci-TestdirectiveCS someArg="something"></dci-TestdirectiveCS>

So, if all went well, when we load the app, we should see the directive look something like this:

Some Value: something

But, that isn't what happens. Here is a screenshot from the Chrome debugger:

The $scope.someArg is undefined. Why is that?

The reason is because directive scope attributes must be defined all in lower case. My desire to make use of 'standard' variable naming conventions killed the attribute.

Create a Directive with Problem 2: Simple Values and Inheritance

Let's fix that issue and go onto our second problem:


angular.module('directiveApp').directive('dciTestdirectivesimplevalue',function(){
return {
restrict: 'AE',
template : "<b>Some Value</b>: {{someValue}}<br/><br/>",
scope : {
somearg : "="
},
controller : function($scope){
$scope.someValue = $scope.somearg;
}
}
});

This directive puts the scope attribute properly in all lowercase. Use it like this:


<dci-TestdirectiveSimpleValue someArg="something"></dci-TestdirectiveSimpleValue>

Now when the controller function is executed the somearg should have the value something, correct? Let's see:

Unfortunately, the issue is still not fixed. This is related to how JavaScript handles inheritance and how Angular creates directive instances. Objects are inherited, but simple values are not. This is why many professional grade directives, such as ngGrid, have you pass in options as part of an options object.

Create a Working Directive

Now that we got those two problems out of the way, here is the proper version:


angular.module('directiveApp').directive('dciTestdirectivecorrect',function(){
return {
restrict: 'AE',
template : "<b>Some Value</b>: {{someValue}}<br/><br/>",
scope : {
somearg : "="
},
controller : function($scope){
$scope.someValue = $scope.somearg.value;
}
}
});

The somearg is defined all in lowercase. When we access that value in the controller, we introspect it to get a property on the somearg object. You can use this directive like this:


<dci-TestdirectiveCorrect someArg="{value:'something'}"></dci-TestdirectiveCorrect>

The someArg attribute creates an object with an attribute named value. Run the code, and you'll see you can now properly introspect the somearg object to get at its properties:

Run this in a browser and you'll something like this:

Play with this code here.

Final Thoughts

In summary, if you are having issues with undefined attributes of an Angular directive, here are two things to check:

  • Make sure the directive attribute is defined in all lowercase.
  • Make sure simple values are wrapped in objects.

I've made both mistakes more than I care to admit.

Sign up for DotComIt's Monthly Technical Newsletter

More Entries

All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.