Tuesday 6 December 2016

Spot The Bug: A Nasty Angular String Surprise

Spot the Bug is back!

Didya miss me?!

Today, we'll be examining something I ran into while experimenting with AngularJS.

AngularJS seemed like a fun thing and useful thing to pick up. I wasn't disappointed on that score. But man, was it ever frustrating in places!

So I had managed to create a drop-down list in AngularJS, and populate it with months of the year. I then set the default value to "May". Upon reloading the page, instead of the value "May", it was displaying a blank. What gives?


What I saw upon reloading the page...

This is the front-end code.
<!DOCTYPE html>
<html>
    <head>
        <title>Angular Test</title>
        <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
        <script src="js/main.js"></script>
    </head>

    <body ng-app="monthApp">
        <div ng-controller="monthCtrl">
            <form id="frmMonth" name="frmMonth" novalidate>
                <label for="ddlMonth" class="control-label">Month </label>
                <select id="ddlMonthFrom" name="ddlMonthFrom" class="form-control" ng-model="monthfrom" required>
                    <option value="">(select one)</option>
                    <option ng-repeat="month in list_months" value="{{month.id}}">{{month.name}}</option>
                </select>
                <button class="btn btn-default pull-right" ng-disabled="frmSchool.$invalid" ng-click="update()">Update</button>
            </form>
        </div>
    </body>
</html>

This is the controller in main.js.
var app = angular.module("monthApp", [])
.run(function($rootScope,$http)
{
    //month list
    $rootScope.list_months=[];
    for (var i=0;i<12;i++)
    {
        $rootScope.list_months.push({id:(i+1),name:getmonthname(i+1)});
    }
});

app.controller("monthCtrl", function($scope,$rootScope,$http)
{
    $scope.list_months=$rootScope.list_months;
    $scope.month=5;
});

function getmonthname(varmonth)
{
    if (varmonth==1) return "Jan";
    if (varmonth==2) return "Feb";
    if (varmonth==3) return "Mar";
    if (varmonth==4) return "Apr";
    if (varmonth==5) return "May";
    if (varmonth==6) return "Jun";
    if (varmonth==7) return "Jul";
    if (varmonth==8) return "Aug";
    if (varmonth==9) return "Sep";
    if (varmonth==10) return "Oct";
    if (varmonth==11) return "Nov";
    if (varmonth==12) return "Dec";
}

When I used my browser to inspect the element, I discovered some code inserted into the drop-down list. Which caused the drop-down list to have a new item and the default value set to that item. Weird, huh.

                    <option value="? number:5 ?"></option>
                    <option value="">(select one)</option>

What went wrong

Apparently, AngularJS code for list item comparisons only recognizes strings. My values were integers. And if none of the items in the list matched the conditional, it would insert one new item in there, with an empty string, just so that conditional would return true.

How I fixed it

Added this quick cheap fix to the code. Instant string conversion! It worked!
app.controller("monthCtrl", function($scope,$rootScope,$http)
{
    $scope.list_months=$rootScope.list_months;
    $scope.month="5";


 

Moral of the story

I'm just gonna say I don't really feel that this one is my fault. This is JavaScript. JavaScript is supposed to be dynamically typed. What AngularJS does, doesn't feel at all consistent with JavaScript in this sense. Seriously, whatever happened to coercion?

Looks like the bug's been caught. High five,
T___T

No comments:

Post a Comment