1

I have a controller which is populating content to content areas using ng-repeat. The issue is that some of this content needs to come front template files and so needs to be compiled 'on the fly'. Right now I have this function dynamically adding content:

$scope.layouts = [

    { id: 'Dashboard', icon: 'dashboard', view: '/qph/views/Dashboard.php' },
    { id: 'Customers', icon: 'people', view: '/qph/views/users.php' },
    { id: 'Quotes', icon: 'format_list_bulleted', view: '/qph/views/Quotes.php' }

];

$scope.workspace = {};    

var getTemplate = function(id){

    var view = 'test.php';

    $timeout(function() { //added timeout

    if($templateCache.get(view) === undefined) {
            $templateRequest(view).then(function (data) {
                $scope.workspaces.forEach(function (v) {
                    if (v.id == id) v.content = $compile(data)($scope);
                });
            });

    } else {
        $scope.workspaces.forEach(function (v) {
            if (v.id == id) v.content = $compile($templateCache.get(view))($scope);
        });

    }
    }, 2000);
};

$scope.workspaces =
    [
        { id: 1, name: "Dashboard", icon: 'dashboard', active:true  }
    ];
getTemplate(1);

I have checked that the data variable has the html content as expected, but the compile is outputting the following:

{"0":{"jQuery331075208394539601512":{"$scope":"$SCOPE","$ngControllerController":{}}},"length":1}

Does anyone know why its not compiling the html content as expected?

Here is the template content for reference:

<div class="col-sm-6 col-sm-offset-3" ng-controller="UserController">
<div class="col-sm-6 col-sm-offset-3">
    <div class="well">
        <h3>Users</h3>
        <button class="btn btn-primary" style="margin-bottom: 10px" ng-click="user.getUsers()">Get Users!</button>
        <ul class="list-group" ng-if="user.users">
            <li class="list-group-item" ng-repeat="user in user.users">
                <h4>{{user.name}}</h4>
                <h5>{{user.email}}</h5>
            </li>
        </ul>
        <div class="alert alert-danger" ng-if="user.error">
            <strong>There was an error: </strong> {{user.error.error}}
            <br>Please go back and login again
        </div>
    </div>
</div>
</div>

Here is the tabs view that is to display the compiled content:

<ul class="nav nav-tabs workspace-tabs">
    <li class="nav-item" ng-repeat="space in workspaces">
        <a class="nav-link" data-toggle="tab" href="#workspace{{space.id}}" ng-class="(space.active == true ) ? 'active show': ''">
            <span class="hidden-sm-up"><i class="material-icons md-24">{{space.icon}}</i></span>
            <span class="hidden-xs-down">{{space.name}}</span>
            <button ng-click="workspace.remove($index)">x</button>
        </a>
    </li>
</ul>

<div class="tab-content workspace-content">
    <div ng-repeat="space in workspaces" id="workspace{{space.id}}" class="tab-pane fade in" ng-class="(space.active == true ) ? 'active show': ''">
        {{space.content}}
    </div>
</div>
6
  • @PatricioVargas This is AngularJS (I said in the title) Commented Sep 14, 2018 at 18:12
  • @PatricioVargas Everything coded here is reference from AngularJS resources. If there is something here that has a better alternative I'm all ears. Commented Sep 14, 2018 at 18:13
  • @PatricioVargas I've just read my post and I've not mentioned component once. I appreciate your looking at this, but I think this might not be something you can assist with, but thanks anyway. Commented Sep 14, 2018 at 18:28
  • How are you attaching the compiled output to the DOM? Commented Sep 15, 2018 at 6:39
  • 1
    @georgeawg An array in the scope is appended and is handled in a ng-repeat loop. I will add this part to the content above. Commented Sep 15, 2018 at 11:36

2 Answers 2

2

The $compile service creates a jqLite object that needs to be added to the DOM with a jqLite or jQuery append() method. Using interpolation {{ }} will only render the stringified value of the jqLite object.

<div class="tab-content workspace-content">
    <div ng-repeat="space in workspaces" id="workspace{{space.id}}" class="tab-pane fade in" ng-class="(space.active == true ) ? 'active show': ''">
        ̶{̶{̶s̶p̶a̶c̶e̶.̶c̶o̶n̶t̶e̶n̶t̶}̶}̶
        <compile html="space.html"></compile>
    </div>
</div>

Instead, use a custom directive to compile and append the HTML data to the DOM:

app.directive("compile", function($compile) {
    return {
        link: postLink,
    };
    function postLink(scope, elem, attrs) {
        var rawHTML = scope.$eval(attrs.html)
        var linkFn = $compile(rawHTML);
        var $html = linkFn(scope);
        elem.append($html);
    }
})

For more information, see AngularJS Developer Guide - HTML Compiler.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for this feedback. I tested this and it works, but only if the process happens quickly, ie the template doesn't take too long to load. It fails to render at all otherwise. I've added a timeout in the code above to simulate this.
If the other answer works for you, why not just use the ng-include directive?
That works! I'm new to Angular and did not know the ng-include directive would handle that, thank you :)
2

Use a directive.

app.directive('myCustomer', function() {
  return {
    templateUrl: 'test.php',
    controller: 'UserController'
  };
})

Template cache will be managed automatically.

1 Comment

Tried this one and it worked, however its static and so unable to receive a template name and return it. I'm testing to making it dynamic, but no luck yet.

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.