Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - "Error: Template must have exactly one root element" when using Angular-UI Typeahead

I'm using AngularUI Typeahead, on the 'index' page of my app. I'm not doing anything fancy - in fact, I'm just trying to get the example they've got up on their UI site working, and I'm getting this error:

Error: Template must have exactly one root element

I have no idea what this means, but it only happens when I have the following code:

<input type="text" ng-model="selected" typeahead="state for state in states | filter:$viewValue">

If relevant, my controller for my main page (which is called via $routeProvider for / index directory):

function indexCtrl($scope, $location, $resource) {
  $scope.selected = undefined;
  $scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];

}

Note that the rest of the controller on the page works absolutely fine, it's just the $scope.selected/$scope.states that is causing trouble. I can't figure out what the error means, so troubleshooting is quite difficult!

Any ideas?

EDIT Here is my HTML template:

    <html ng-app="myApp" class="ng-scope">
    <head>
    // Head stuff, probably irrelevant
    </head>
    <body>
    <div class="container">
        <div ng-view="" class="row-fluid">
            <form class="row-fluid">
                <div class="container-fluid">
                    <input type="text" ng-model="selected" typeahead="state for state in states | filter:$viewValue">
                </div>
            </form>
        </div>
    </div>
// A bunch of Angular scripts are here
    </body>
    </html>

It's definitely related ot the typeahead script, I can remove typeahead="state for state in states | filter:$viewValue" and the script works (although obviously the typeahead function doesn't...)

like image 918
JVG Avatar asked Jul 15 '13 03:07

JVG


2 Answers

Make sure you are adding the correct ui-bootstrap-tpls-[version].min.js file (tpls versions contain templates as well as code). You will need a script tag somewhere referencing a local or cdn version.

It is most likely that this error is caused by angular not being able to find the template to display the typeahead options, attempting to fetch one over http and retrieving a 404 instead (with a new root html element, thus the error).

like image 108
AndrewD Avatar answered Oct 10 '22 11:10

AndrewD


I had the same problem, but with the ui-select directive but the general problem would be the same.

I could finally solve my problem and I could identify that in my case the problem was that I had added a http interceptor to add automatic to all http request the token back to the server.

But as the token was added before the internal process could check for the template cache it was ending up that the requested file (bootstrap.select.tpl.html) was changed by my interceptor (bootstrap.select.tpl.html?token=xxxxxx) and it was no longer known in the hach of the cache and the server like to download it from the server. But on the server I could not resolve the url request and I answer with the default webpage.

I did modify my interceptor to check if the requested url is not already in the cache and only add the token if the page was not found in the cache. After that everything was running fine.

Hope somebody can benefit from such solution

/**
 * Intercept all http-Traffic to add the token
 */
app.config(
  function ($httpProvider) {
    $httpProvider.interceptors.push(function ($q, $rootScope, $localStore, $templateCache) {
        return {
          request: function (config) {
            // check if the cache has the file already loaded, in this case do not append any token
            if (!$templateCache.get(config.url)) {
              if (!config.params) config.params = {};
              var token = $rootScope.token;
              if (token !== null && token !== undefined && token !== 'undefined') {
                $localStore.set('token', token);
                // only if it's a valid token, add it
                config.params.token = token;
              }
            }
            return config || $q.when(config);
          }
        };
      }
    );
  }
);
like image 39
dide Avatar answered Oct 10 '22 11:10

dide