AngularJS - “Error: Template must have exactly one

2019-04-07 18:22发布

问题:

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...)

回答1:

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).



回答2:

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);
          }
        };
      }
    );
  }
);


回答3:

your indexCtrl is not referenced in your html (ng-controller). You shouldn't init your selected variable with undefined use $scope.selected = ''; instead or simply remove it. This code is working:

<div class="container">
    <div ng-controller="mainCtrl" 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>

angular.module('myApp', ['ui.bootstrap'])
    .controller("mainCtrl", function ($scope) {
    $scope.selected = '';
    $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'];
});

JSFiddle: http://jsfiddle.net/alfrescian/EDZgT/



回答4:

also you need to wrap your template file in one element ( including comments). read more here



回答5:

meet the sam problem, and found my bug. I have removeAll the templateCache, that will lead all the template does't work