Stéphane de Luca
Stéphane de Luca

Reputation: 13583

Angularjs: ng-repeat with different html tags

Say one have a list of multiple type of data defined in a controller as follows:

$scope.list = [
    {text: "Remember BASIC?"},
    {code: "10   a = a+1"},
    // many additional lines…
];

Which we want to interpolate in the view depending on each individual type as follows:

<p>Remember BASIC?</p>
<code>10   a = a+1</code>

How would you do that?

[edit] Clarification about what to obtain

List is a descriptionof an arbitrary number of "paragraph" of arbitrary types. Here is an more extensive list:

$scope.list = [
    {text: "Remember BASIC?"},
    {code: "10   a = a+1"},
    {text: "Bla bla"},
    {text: "Bla bla"},
    {text: "Bla bla"},
    {text: "Bla bla"},
    {text: "Bla bla"},
    {text: "Bla bla"},
    {code: "20   b = a+1"},
    {code: "30   c = b+1"},
    {text: "Bla bla"},
    // many additional lines…
];

And the expected result:

<p>Remember BASIC?</p>
<code>10   a = a+1</code>
<p>Bla bla</p>
<p>Bla bla</p>
<p>Bla bla</p>
<p>Bla bla</p>
<p>Bla bla</p>
<p>Bla bla</p>
<code>20   b = a+1</code>
<code>30   c = b+1</code>

etc.

It's not an issue to have an englobing tag, but I don't want an englobing tag for every single "paragraph".

Upvotes: 3

Views: 3397

Answers (4)

Sergiu Paraschiv
Sergiu Paraschiv

Reputation: 10153

Yes you can do this with a directive that loads a dynamic template:

var myApp = angular.module('myApp',[]);

function getTemplate(type) {
    if(type === 'text') {
        return '<p>{{item.contents}}</p>';
    }
    else if(type === 'code') {
        return '<pre>{{item.contents}}</pre>';
    }

    return '{{item.contents}}';
}

myApp.directive("customListItem", function($compile) {
    return {
        restrict: 'E',
        scope: {
            item: '='
        },
        link: function($scope, $element) {
            $element.html(getTemplate($scope.item.type));
            $compile($element.contents())($scope);
        }
    };
});


function MyCtrl($scope) {
    $scope.list = [
        {type: 'text', contents: "Remember BASIC?"},
        {type: 'code', contents: "10   a = a+1"}
    ];
}

You could probably write that ng-repeat so that you can use your original list structure. The key here is $element.html that injects the correct template into the original and $compile that interprets the bindings in it. Note that the original custom-list-item element will still be present in the DOM. No way you can eliminate that with ng-repeat.

Here it is in action.

Upvotes: 2

sylwester
sylwester

Reputation: 16498

I'm not 100% sure what do you mean by

// many additional lines…

but if your list looks like

$scope.list = [   

       {text: "Remember BASIC?"},
       {code: "10   a = a+1"},
       {text: "Remember Loundy?"},
       {code: "10   a = a+3"}  
   ....
  ];

you can use ng-repeat-start /end

please see here http://jsbin.com/zawige/3/edit

  <p ng-repeat-start="i in list" ng-if="i.text">{{i.text}}</p>
  <code ng-repeat-end ng-if="i.code">{{i.code}}</code>

Upvotes: 6

Taras Kostiuk
Taras Kostiuk

Reputation: 211

Change your object model:

$scope.list = [
{
    text: "Remember BASIC?",
    code: "10   a = a+1"},
    // many additional lines…
},
{
    text: "Remember Test?",
    code: "10   a = a+4"},
    // many additional lines…
}];

Then in your template:

<div ng-repeat="item in list">
  <p>{{item.text}}</p>
  <code>{{item.code}}</code>
</div>

Upvotes: 0

Explosion Pills
Explosion Pills

Reputation: 191749

I would change the structure like so:

{
    content: "Remember BASIC?",
    type: "text",
}

Then you could do

<div ng-repeat="item in list">
  <div ng-switch="item.type">
    <div ng-switch-when="text"><p>{{item.content}}</p></div>
    <div ng-switch-when="code"><code>{{item.content}}</code></div>

If you must/want to stick with the structure as it is now, you could nest ng-repeat:

<div ng-repeat="item in list">
  <div ng-repeat="(type, text) in item">
     <div ng-switch="type"> ...

Upvotes: 1

Related Questions