Tag Archives: angularjs

AngularJS 1.6 Bindings Inconsistent? No, just RTFM.

When upgrading our major project from AngularJS 1.5 to 1.6, I read through the release notes when I was fatigued and the impact of the changes to $compile didn’t sink in.

As changes are made to this large project, code is being adapted towards better Angular (2+) compatibility. I added a new controller in TypeScript and used the controller as approach of binding to the controller object rather than $scope. Without thinking what I was doing, I set a default value for a bound class variable in the class definition:

class MyController {
    public somethingAwesome: string = DefaultStringValue

In the template HTML:

<input ng-model="$ctrl.somethingAwesome">

And “somethingAwesome” became undefined no matter how  much anybody pounded on the keyboard.

The release notes for $compile state, “Due to bcd0d4, pre-assigning bindings on controller instances is disabled by default”, and reminds us, “Initialization logic that relies on bindings being present should be put in the controller’s $onInit<() method, which is guaranteed to always be called after the bindings have been assigned."

Having been chastised by the documentation for my lapse, I sheepishly added an $onInit() method to the class and quietly checked it in.

 

Display a Server-Supplied Drop Down List Using AngularJS

These are my notes on displaying a list of server-supplied objects in a drop down list using AngularJS.

Background

I have a server that supplies lists of lookup objects that are used in an AngularJS-based single-page application (SPA). The SPA obtains a list through an API call to the server. The server returns an ordered list of JSON objects. Every object in every list includes a key value, a display value, and supplementary data. For the purposes of this article, only the key and display values are of any concern.

For example, the SPA needs a list of units of measure. The server supplies a list of objects along these lines. They key value is called code and the display value is called display:

[javascript light=”true”]
[
{
code: "L",
display: "L",
description: "litres"
},
{
code: "ML",
display: "mL",
description: "millilitres"
},
… etc …
]
[/javascript]

In the SPA code, each lookup table is wrapped in its own Angular service.

From List of Objects to Dropdown Using <select>

Angular can be told to create a dropdown list using an array of objects thus:

[html light=”true”]
<select ng-model="product.uom"
        ng-options="u.display for u in units" />
[/html]

Here ng-options tells Angular to build the dropdown list showing the display attribute of each object. Whenever the user chooses an item, the entire associated object is stored in $scope.product.uom (uom means units of measure). For my purposes this is very handy since I want access to the entire object.

Defaulting to a Value

This works beautifully until an edit page is shown. When displaying data from the server, the dropdown shows a blank selection even though $scope.product.uom contains an object with all the correct values!

The problem is that Angular matches based on object references, not object contents. This can be illustrated thus:

[javascript]
var a = {foo: "bar"};
var b = {foo: "bar"};
var c = a;
[/javascript]

Variables a and b contain two separate objects that by chance have attributes with the same values. Variables a and c contain the same object pointer.

In the example above, Angular will recognize the value in $scope.product.uom only if it points to an object in the master list $scope.units. The fact that the server-supplied object has identical attributes is irrelevant — Angular only cares whether the object pointers are identical.

[Edit]

There are a couple of ways around this. If there is a unique key on the object, one can use the AngularJS track by clause to tell AngularJS to compare the keys. Continuing the above examples, each object has a code attribute that has a unique string which JavaScript can compare.

[html light=”true”]
<select ng-model="product.uom"
ng-options="u.display for u in units track by u.code" />
[/html]

[Original Article]

Otherwise,  when an object is loaded from the server for editing, the lookup values are replaced with pointers to the corresponding objects in the dropdown list. An unsophisticated but functional bit of code to perform this substitution might be:

[javascript]
// Wrapper function to retrieve a product
// from the server, keyed on productId.
apiProduct.lookup(productId, function(product) {
$scope.product = product;

// Replace the server-supplied lookup value
// with the matching value
// in the $scope.units array.
$scope.product.uom = lookup_by_code(product.uom.code, $scope.units);
});

function lookup_by_code(code, data) {
for(var i=0; i
if(data[i].code == code)
return data[i];

return null;
};
[/javascript]

Update

There is a JSFiddle that demonstrates the value/reference problem concisely.

How to Access a Local Node Server Using Websockets

Background

The AngularJS web application that I’m working on runs on a remote server, but needs to access laboratory instruments connected to the local computer that is running the web browser. JavaScript running in the web browser runs in a sandbox and is prohibited from accessing local hardware.

We explored several possibilities of how to work around this and found a fairly simple solution. The local computer runs a small Node.js program to act as glue between the instrument and the local web browser. Node.js communicates with the instrument’s USB serial port using the Node serialport plugin.

Node also runs express to serve up a simple AngularJS web application for diagnostics. We also connect socket.io to the express instance to provide an interactive communication pipeline between the Node.js program and the main web application.

The Problem Space

One of the traditional iron clad security paradigms of web programming is that JavaScript served up from a server cannot access another server. This works for nearly all web sites, but there are instances where being able to share resources across servers is desirable. For example, if our web app can communicate with local laboratory instruments it’s a big win for my client.

The Approach

The W3C has published Cross-Origin Resource Sharing specifications which provide a standardized method for doing this. To implement this, the non-origin server (in our case, the Node.js server) has to provide HTTP headers to the web browser indicating that it will accept the cross-origin request.

If these headers are missing, the web browser will not complete the HTTP request. For example, Firefox 29 — in its debug console — will report

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8888/socket.io/1/?t=1402443850582. This can be fixed by moving the resource to the same domain or enabling CORS.

This means that the web browser is denying access to Node.js running on localhost because (a) it is a different destination than the original server (the origin) and (b) the Node.js server is not granting permission for the cross-origin request.

Thus the problem boils down to coaxing socket.io to provide those headers when the web browser connects.

The Snag

I have not been able to get this to work on socket.io version 1.0 and higher. To avoid wasting time I reverted to pre-1.0 thus:

npm install --save socket.io@"<1.0"

In the Node.js program’s main app.js, I added one line to allow connections from any cross-origin server (see line 2 below). Note that this is development code running on an isolated network inaccessible from the Internet. One should think hard before leaving this open to all comers.

var io = require('socket.io').listen(server);
io.set('origins', '*:*');
server.listen(8888);

If you look in the socket.io source file ./lib/manager.js you’ll see the lines:

  if (origin) {
    // https://developer.mozilla.org/En/HTTP_Access_Control
    headers['Access-Control-Allow-Origin'] = origin;
    headers['Access-Control-Allow-Credentials'] = 'true';
  }

This may prove useful during debugging if adding the set('origins' ... call doesn’t work as expected.

Unanswered Questions

This solution doesn’t appear to work for serialport.io version 1.0 and higher.

References

Cross-Origin Resource Sharing official W3C documentation.

Using CORS introduction to CORS.

Enable Cross-Origin Resource Sharing sample code.

Socket.io doesn’t set CORS header(s) on Stackoverflow

 

 

 

 

ui-bootstrap Oddity

The <tab> element from the Angular-UI project’s ui-bootstrap has an oddity with the activate attribute, it seems, when calling a function to determine whether a tab should be active. For example,

[javascript light=”true”]

<tab ng-repeat="u in foo.units" active="activeTab(u)">

[/javascript]

causes JavaScript errors and the tabs do not function correctly. Poking around in the internals of ui-bootstrap-tpls.js lead to me discover that in this case the code

[javascript light=”true”]
if (attrs.active) {
getActive = $parse(attrs.active);
setActive = getActive.assign;
[/javascript]

leaves setActive to undefined. The JavaScript later attempts to call setActive, which crashes and burns of course.

I was able to work around the problem by copying the essentials of the activeTab function into the active attribute, e.g.

[javascript light=”true”]

<tab ng-repeat="u in foo.units" active="bar.isPlugh == u.state.isPlugh">

[/javascript]

AngularJS Directives: Expression or Interpolation?

AngularJS seems to be inconsistent on whether a directive can contain an expression or requires an expression to be wrapped in interpolation brackets {{ thusly }}.

Here is a partial list of directives consisting of ones that have bitten me.

Directive Expression or Interpolation? Notes
ng-class Expression Accepts an object, array or string.
ng-src Interpolation
ng-href Interpolation
ng-srcset Interpolation
ng-style Expression Give it an object.
ng-include Interpolation Accepts a string.

Django and AngularJS POST

I’m converting some code that uses jQuery to post data to Django over to AngularJS. The POSTed data wasn’t coming across as expected.

By default AngularJS and jQuery use two different methods of packaging data. According what I’ve gleaned from googling:

  • jQuery posts using a content type of application/x-www-form-urlencoded and serializes the data using the foo=bar&fuz=baz method.
  • AngularJS posts using a content type of application/json and serializes the data in the body.

Because this is an Angular single-page application, rather than using Django’s templating, I’m making calls to a web-based API (implemented in Django). I use a simple helper class for the repetitive task of extracting POSTed data, converting it to Python objects, and validating it.

I made a simple change to this object, and voilà — Django started receiving AngularJS data.

[python]
# CONTEXT:
# This function extracts the received data and
# returns it as a Python dict.
if ‘CONTENT_TYPE’ in request.META and request.META[‘CONTENT_TYPE’].startswith(‘application/json’):
return json.loads(request.body)
elif request.method == ‘POST’:
return request.POST.dict()
elif …
[/python]

If you wish to use Django’s unit testing to exercise this, use something like the following. Remember to send a JSON serialized string into post() as it will not do this for you.

[python]
from json import dumps
from django.test.client import Client


client = Client()
self.assertTrue(client.login(username=user, password=password))
response = client.post(url, dumps(data), content_type=’application/json’)
[/python]

Updates

2014-02-21
Fixed bug in sample code.
2014-06-05
Added unit testing example.