Search code examples
angularjsrestangularjs-ng-repeatspring-datarestangular

Resolving object dependencies on client using Restangular


I have problems with restoring my entity relations at the (AngularJS) client after retrieving them via REST using Restangular. I searched a lot, but could not find a clean solution. I have some years of programming experience, but I'm quite new to the AngularJS ecosphere and the REST paradigm.

The task

At my Spring Data backend I have two entities, Article and ArticleGroup, in a 1:n relationship. The resulting REST json (via Spring Data Rest) looks like this:

Article

{
"_embedded": {
    "articles": [
        {
            "id": 1,
            "name": "A Drink",
            "price": 2.90,
            "created": null,
            "modified": null,
            "active": false,
            "displayOrder": 0,
            "_links": {
                "self": {
                    "href": "http://localhost:8080/articles/1"
                },
                "articleGroup": {
                    "href": "http://localhost:8080/articles/1/articleGroup"
                }
            }
        },
      ...

ArticleGroup

{
"_embedded": {
    "articleGroups": [
        {
            "id": 1,
            "name": "Drinks",
            "printer": null,
            "_links": {
                "self": {
                    "href": "http://localhost:8080/articleGroups/1"
                },
                "articles": {
                    "href": "http://localhost:8080/articleGroups/1/articles"
                }
            }
        },
      ...

Now I want to display a tabgroup containing the articleGroup.name as the tab's label and a table of the articles in this articleGroup as its content:

<tabset>
        <tab ng-repeat="group in articleGroups" heading="{{group.name}}">
            <table class="table">
                <tr ng-repeat="article in group.articles">
                    <td ng-click="increase(article)">{{article.name}}</td>
                    <td>{{article.price | currency }}</td>
                    ...
                </tr>
            </table>
        </tab>
</tabset>

I retrieve the articleGroups easily in my AngularJS controller using Restangular:

Restangular.all('articleGroups').getList()
        .then(function(groups) {
            $scope.articleGroups = groups;
        });

This works fine and the tabs show up nicely. Now I can do so for the articles as well, but here I come upon the problem which I'm dealing with for days now.

The problem

How can I filter the articles for their articleGroup so every portion of articles appears in the right tab? What is the right expression in the ng-repeat above where I just put "group.articles" as a placeholder now? This sounds very easy, but my problem is that I have no operational identification of an Article's articleGroup to filter the right articles in the ng-repeat for each tab. What I tried:

  • using the self.href as an id à la article._links.articleGroup.href == articleGroup._links.self.href, but this doesn't work as you can see in the JSON above those two links are set up differently
  • adding (database) ids to the JSON, but of course an article does not contain the id of its articleGroup, but only the link relation
  • looping through articleGroups and retrieving each group's articles into an array with the articleGroup.id as the key, but I can't get this to work out as I either get undefined errors by JavaScript or infinite loops

Help

Before I continue fiddling along for hours, it would be great if you could give me hints on which direction to take and what a "clean" and methodologically sound approach to the task would be. How can I bring articles and their corresponding group back togehter? It seems like such an easy (and frequent) problem, but I tried for hours and days to get it running and fear I'm missing something. I looked at many examples but none of them helped me to have a breakthrough. I'm happy to provide any further information as needed. Thanks a lot in advance!


Solution

  • Well, I solved it. It turned out that I got lost in the handling of asynchronous requests, especially between having a data object itself and a promise for a request, i.e. placeholders which provide access to the data as soon as it is available - very well explained in this post.

    Restangular always returns promises. So I came up with the following solution which works exactly as I wanted to:

    For the controller code:

    Restangular.all('articleGroups').getList()
            .then(function(groups) {
                $scope.articleGroups = groups;
                for(i=0;i<groups.length;i++) {
                    $scope.articles[groups[i].id] = groups[i].getList("articles")
                }
            });
    

    So when the articleGroups arrive from the server, I walk through all of them and put them into the array $scope.articles with the articleGroup.id as key. Notice that group[i].getList("articles") returns a promise for the articles relation. With Restangular's $object property, I receive the articles data array in the ng-repeat:

        <tabset>
            <tab ng-repeat="group in articleGroups" heading="{{group.name}}">
                <table class="table">
                    <tr ng-repeat="article in articles[group.id].$object | orderBy:'id':false ">
                        <td>{{article.name}}</td>
                        <td>{{article.price | currency }}</td>
                    </tr>
                </table>
            </tab>
        </tabset>
    

    Now, all articles of the selected articleGroup's tab are displayed on the tab. While this seems fine, I'm still not sure if this is the best solution in terms of style or performance. But at least it's a step ahead. Comments very welcome.