1

I am experienced as a developer but a beginner in angularjs and I have not used javascript for a while.

My site is separated into several angularjs apps. But two apps could have to display the same "entity" types.

For example my first app is dedicated to books and would receive from an api (for example:http://example.com/Books) the following json data:

[
    { "id":"1", "title":"...", "publisher":"..." },
    { "id":"2", "title":"...", "publisher":"..." },
]

The second app is more general and would receive from an api (for example:http://example.com/Things) the following json data:

{
    "count":"3",
    "items":
        // my two books
        { "type":"book", "data":{ "id":"1", "title":"...", "publisher":"..." } },
        { "type":"book", "data":{ "id":"2", "title":"...", "publisher":"..." } },
        // and other things
        { "type":"movie", "data":{ "id":"45", "title":"...", "producer":"..." } },
    ]
}

I created a template "book.html" :

<div>{{book.title}} ({{book.publisher}})</div>

I use it in my view with :

<ul><li ng-repeat="book in books" ng-include="'book.html'"/></ul>

So far so good...

I now would like to use the same template - without duplicating it - to display the books in the first case and in the second with something like :

<ul><li ng-repeat="item in items" ng-include="item.type+'.html'"/></ul>

My issue is not dynamic templates. My issue is: in my book.html template, I use "book.title", but for my second app, I would need to use "item.data.title".

The ideal, in my mind, would have been something like :

<ul><li ng-repeat="item in items" ng-include="item.type+'.html'" ng-something="book=item.data"/></ul>

I could resolve it by "converting" my books array into the second format. But I think I misunderstood something and perhaps I am using angularjs in a wrong way.

Could you please give me some clues ?

Thanks

1

3 Answers 3

3

Use directives. They look intimidating at first, but they are quite simple for such use cases:

app.directive("book", function() {
    return {
        restrict: "A",
        templateUrl: "book.html",
        scope: {
            book: "="
        }
}); // isnt' it simple?

First case:

<ul>
    <li ng-repeat="book in books">
        <span book="book"></span>
    </li>
</ul>

Second case:

<ul>
    <li ng-repeat="item in items" ng-switch="item.type">
        <span ng-switch-when="book" book="item.data"></span>
        <span ng-switch-when="movie" movie="item.data"></span>
        ...
    </li>
</ul>
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. It is a good alternative of Ivarni's answer. I am reading more about directives although their declaration seems quite unreadable (or intimidating as you say).
@Loic Directives can be complicated things, but the sooner you start working with them the sooner you'll be able to leverage them.
0

Angular has a built-in directive called ng-init that can be used to evaluate expressions on a scope, meaning it can be used to set scope-variables without having to create a controller or a custom directive.

In this case

    <li ng-repeat="item in items" 
        ng-include="item.type+'.html'" 
        ng-init="book=item.data"/>

Should do the trick, although the documentation specifies that it is not intended for this use-case:

The only appropriate use of ngInit is for aliasing special properties of ngRepeat [$index, $even etc], as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.

https://docs.angularjs.org/api/ng/directive/ngInit

3 Comments

Actually, this is not what ng-init is for. The "special properties" referred to are $index, $even etc, which are overridden in nested ng-repeat directives - hence, you may need to alias them.
@AlexG On second read-through, it seems I misunderstood the docs the first time I read it. I would still argue that this is an acceptable use of ng-init though and by far the least verbose way of solving this case.
I agree it does work for this simple case and I am guilty of using it when I can't be bothered to write a controller or directive.
0

If you don't want to change the structure of your templates, you will need to change the structure of your data. Create a controller:

app.controller('BookOrMovieController', function($scope) {
    if ($scope.item.type == 'Book') {
        $scope.book = $scope.item.data;
    }
    if ($scope.item.type == 'Movie') {
        $scope.movie = $scope.item.data;
    }
});

Then instantiate the controller on the included template element:

<ul><li ng-repeat="item in items" ng-include="item.type+'.html'" ng-controller="BookOrMovieController"/></ul>

Comments

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.