Code Apprentice
Code Apprentice

Reputation: 522

When using Angular typeahead directive, console displays error "Template for directive 'uibTypeaheadPopup' must have exactly one root element."

It seems like there are a lot of related questions - however, I've tried everyone of the solutions to no avail.

Let me start by discussing the problem and what I've tried:

I literally copied and pasted the sample from the official angular bootstrap website

HTML

<input type="text" ng-model="selected" uib-typeahead="state for state in states | filter:$viewValue | limitTo:8" class="form-control">

Controller

$scope.states = [...]; // omitted for brevity

And I got the following error:

Error: [$compile:tplrt] Template for directive 'uibTypeaheadPopup' must have exactly one root element. uib/template/typeahead/typeahead-popup.html

I'm not sure if it's relevant, but these are the versions of the relevant libraries involved: - Bootstrap CSS 3.3.7 - jQuery 3.1.1 - Bootstrap.js 3.3.7 - Angular 1.5.7 (I'm using Angular 1 here) - Angular Bootstrap 2.2.0

What I tried:

Attempt 1: Wrapping it in div tags because it would then have "one root element".

Attempt 2: Wrapping the template (typeahead-popup.html) in div tags in the ui-bootstrap-tpls.js angular bootstrap template file.

Attempt 3: I tried removing all the '\n' from the template file because I stumbled upon a site that suggested this as a workaround. The template looks a bit like this in the source file and I got rid of all of the \ns:

angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("uib/template/typeahead/typeahead-popup.html",
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">**\n**" +
"    <li class=\"uib-typeahead-match\" ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">**\n**" +
"        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>**\n**" +
"    </li>**\n**" +
"</ul>**\n**" +
"");
}]);

Attempt 4: I then thought about my templating system, and thought I might have had tags that weren't properly closed. A preliminary inspection came up with an additional <!DOCTYPE html> tag on top of an <html> tag, which I removed, but to no avail. I didn't see any further tags that were not properly closed.

Attempt 5: I thought this might have something to do with the fact that I'm using node.js to run the project, so instead of running localhost, I replaced all my javascript libraries from relative paths to CDNs, and my project files with absolute paths and opened it directly in the browser, but I had problems with cross-origin in Chrome and Firefox.

My <head> section looks something like this.

<head> 
  <!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
  <!--<link href="javascripts/bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet"> &lt;!&ndash;3.3.7&ndash;&gt;-->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">


  <!--<script src="javascripts/bower_components/jquery/dist/jquery.js"></script> &lt;!&ndash;3.1.1&ndash;&gt;-->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

  <!--<script src="javascripts/bower_components/bootstrap/dist/js/bootstrap.js"></script> &lt;!&ndash;3.3.7&ndash;&gt;-->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

  <!--<script src="javascripts/bower_components/angular/angular.js"></script> &lt;!&ndash;1.5.8&ndash;&gt;-->
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>


  <!--<script src="javascripts/bower_components/angular-bootstrap/ui-bootstrap.js"></script> &lt;!&ndash;2.2.0&ndash;&gt;-->
  <!--<script src="https://github.com/angular-ui/bootstrap/blob/gh-pages/ui-bootstrap-tpls-2.2.0.min.js"></script>-->
  <script src="/Users/Baggio/WebstormProjects/Scratchpad/public/javascripts/bower_components/angular-bootstrap/ui-bootstrap.js"></script> <!--2.2.0-->


  <script src="/Users/Baggio/WebstormProjects/Scratchpad/public/javascripts/AppCtrl.js"></script>

Attempt 6: Removing comments, as per Angular's official documentation. I tried removing all the comments in the related file (I'm using ui-router for routing, so for my project, it's the home page, and the immediate template for a child state nested one level down). However, It didn't seem to do the trick.

Attempt 7: I tried replicating the project and stripping down to its bare essentials so I'd only have one html (or ejs as the templating system I'm using) page and one javascript file for the project but the same error showed (...must have exactly one root element).

HTML

<html>
<head>
... // references included under "Attempt 5"
</head>
  <body ng-app="ctl" ng-controller="AppCtrl">
  <div>
      <input type="text" ng-model="selected" uib-typeahead="state for state in states" class="form-control">
  </div>
  </body>
</html>

JS

angular.module("ctl",
    [
        'ui.bootstrap'
    ])
    .controller("AppCtrl", AppCtrl);

AppCtrl.$inject = ["$scope", "$log"];

function AppCtrl($scope, $log) {
    $scope.states = [...]; // omitted for brevity
}

I feel like I'm missing something obvious but I can't figure out what it is...would appreciate some help.

Upvotes: 1

Views: 430

Answers (1)

Code Apprentice
Code Apprentice

Reputation: 522

Actually I just clicked on a link suggested by SO AngularJS - "Error: Template must have exactly one root element" when using Angular-UI Typeahead and got my answer.

It was something very fundamental...as suggested by AndrewD and mattwad, it's simply a matter of including the right dependencies and the right JS files, both of which I missed.

Specifically, ui-bootstrap-tpls.js (template files) ["ui.bootstrap.tpls"] (dependency)

Not sure if this is appropriate to keep this question but someone might still find this helpful?

Upvotes: 1

Related Questions