4

I just can't get it working. Simplified everything and placed it in fiddle: http://jsfiddle.net/svdoever/94aQH/1/.

I want to render a hierarchical chapter of a book which containg paragraphs, and paragraphs can contain sub-paragraphs.

Code:

angular.module("myApp", []).controller("TreeController", ['$scope', function($scope) {
$scope.vm = {};
$scope.vm.chapter = {
        "Id": "N7313",
        "Title": "1 Chapter1",
        "Content": "<p>Contents1</p>",
        "Paragraphs": [
            {
                "Id": "N1.1",
                "Title": "1.1 Paragraph1.1",
                "Content": "<p>Content2</p>",
                "Paragraphs": [
                    {
                        "Id": "N1.1.1",
                        "Title": "1.1.1 Paragraph1.1.1",
                        "Content": "<p>ContentA</p>",
                        "Paragraphs": []
                    },
                    {
                        "Id": "N1.1.2",
                        "Title": "1.1.2 Paragraph1.1.2",
                        "Content": "<p>ContentB.</p>",
                        "Paragraphs": []
                    }
                ]
            },
            {
                "Id": "N1.2",
                "Title": "1.2 Paragraph1.2",
                "Content": "<p>Content3.</p>",
                "Paragraphs": []
            }
        ]
    };
}]);

And html:

<div ng-app="Application" ng-controller="TreeController">
  <script id="paragraphTmpl.html" type="text/ng-template">
    <a class="anchor" ng-attr-id="data.Id"></a>
    <h4 ng-bind="data.Title" />
    <div class="paragraph-text" ng-bind-html="data.Content"></div>
    <!-- Want to loose the div in the repeat as well -->
    <div ng-repeat="paragraph in data.Paragraphs" ng-include="paragraphTmpl.html"></div>
  </script>

  <div class="bookchapter">
    <a class="anchor" ng-attr-id="vm.chapter.Id"></a>
    <h3 ng-bind="vm.chapter.Title" />
    <div class="chapter-text" ng-bind-html="vm.chapter.Content"/>
    <div ng-repeat="paragraph in vm.chapter.Paragraphs" ng-include="paragraphTmpl.html"/>
  </div>
</div>

I also don't want a div rendered in the repeat as specified in the comment. I know how to do this with knockout, but couldn't find it for AngularJS.

Help would be appreciated.

2 Answers 2

2

If you use child scopes to manage the recursion with arbitrarily large size I think you'll need to use the element. How else can you have data refer to different objects, if they all share the same parent element?

Therefore I think you have two options: create a custom directive that has a compile function (which I'm not super familiar with) or hard-code the paragraphs to have a maximum depth of recursion (using ngIf like in my plnkr to remove the unused <div>'s). Here's a good question to get you started. (and of course reading Angular's documentation on directives will be helpful, if you haven't already).

I fixed your code to do everything but remove the extra div's.

http://plnkr.co/edit/ONnvYj91HRSvboWUxDg2

<div ng-app="Application" ng-controller="TreeController">
  <script id="paragraphTmpl.html" type="text/ng-template">
    <a class="anchor" ng-attr-id="data.Id"></a>
    <h4 ng-bind="data.Title"></h4>
    <div class="paragraph-text" ng-bind-html="data.Content"></div>
    <!-- Want to loose the div in the repeat as well -->
    <ng-include
      ng-if="data.Paragraphs.length > 0" 
      onload="data = paragraph" 
      ng-repeat="paragraph in data.Paragraphs" 
      src="'paragraphTmpl.html'"></div>
  </script>

  <div class="bookchapter">
    <a class="anchor" ng-attr-id="vm.chapter.Id"></a>
    <h3 ng-bind="vm.chapter.Title"></h3>
    <div class="chapter-text" ng-bind-html="vm.chapter.Content"></div>
    <ng-include  
      ng-repeat="paragraph in vm.chapter.Paragraphs" 
      ng-if="vm.chapter.Paragraphs.length > 0"
      src="'paragraphTmpl.html'" 
      onload="data = paragraph"/>
  </div>
</div>
Sign up to request clarification or add additional context in comments.

4 Comments

Great, you got it working! It is a pity I have to introduce that extra div... In knockout I solved it using: <!-- ko template: { name: 'paragraphTmpl', foreach:chapter().Paragraphs } --> <!-- /ko -->
I overlooked it, the div's I wanted removed are gone as well... You will get the accept, but one question: why do you do the extra test for length of Paragraphs? The ng-repeat already works if the array is empty. Any ( performace?) reason for that?
Thanks! Are you referring to the ngIf inside of the <div> or the <script> or both?
Hmm well maybe it's not necessary anymore. If I recall it made it freeze without it. Oh that was probably when I had the thing as a directive that had a custom compile function. Yeah so it's not necessary anymore. Good catch.
1

This fiddle just about achieves what you want http://jsfiddle.net/uBMKr/1/

The key is that you were missing '' around your template src name in ng-include. This is because without the quotes, it is looking for a reference to a scope variable.

<ng-include src="mysrc"></ng-include> - looking for $scope.mysrc

<ng-include src="'mysrc'"></ng-include> - looking for a template named 'mysrc'

The only thing it doesn't do is remove the ng-repeat <div>, which I don't think you are able to do (easily at least).

2 Comments

Hi MWay, thanks for the hint on using the single quotes within the double quotes, I missed that. Your fiddle does not iterate into the hierarchy however. Tried to get that working, but could not get it working.
Sorry about that, I updated the link to fix my typo :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.