Search code examples
ruby-on-railsangularjsrestangular

How to work with nested data angularjs and rails?


I am working on an rails application to manage and present images to friends and family.

In that application you can have

Events -> Subevents -> EventImages

routes.rb

resources :events do
  resources :subevents do
    resources :event_images
  end
end

The angularjs part/page is starting when a user selects an specific event.

On the event edit page i want to present the subevents and images like that:

Subevent1 -> Upload Images Button
  Image1 Image2 Image3 ...

Subevent2 -> Upload Images Button
  Image1 Image2 Image3 ...

...

New Subevent Button

So basically i want to present all available subevents and the images inside each subevent on one page (there could be several subevents and several 100 images). The user should be able to add new subevents and to upload or delete images to each subevent and moving images between subevents via drag/drop. But adding/deleting/uploading/mowing is not my problem right now i just mentioned it because it might influence the solution to my problem.

Im using a nested ng-repeat to display all the information on the page

  <div class="subevent" ng-repeat="subevent in event.subevents">
          <li class="img" ng-repeat="image in subevent.event_images">
          </li>
  </div>

Im new to the angular world and im having problems now on how to retrieve the data i need for the page mentioned above.

I came up with two ideas so far:

Retrieve all the informations via an api controller in an nested form:

show.json.jbuilder

json.id @event.id
json.subevents @event.subevents do |subevent|
  json.id subevent.id
  json.name subevent.name
  json.event_images subevent.event_images do |event_image|
    json.id event_image.id
    json.thumb event_image.image({ resize: "150x150" }).url
  end
end

Which will give me this:

{
    "id": "54d75dd9686f6c2594020000",
    "subevents": [
        {
            "id": "54d75de1686f6c2594030000",
            "name": "Test",
            "event_images": [
                {
                    "id": "54df3904686f6c41cf0c0000",
                    "thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
                },
                {
                    "id": "54df56c5686f6c41cf850100",
                    "thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
                }
            ]
        }
    ]
}

And im using restangular to retrieve this information

$scope.event = Restangular.one('api/events','<%= @event.id %>').get().$object;

But this solution doesnt feel right to me. When i start to manipulate the page (uploading new images/deleting images/moving images from one subevent to another) i see myself refreshing the complete page because i see no other way here on how to just delete one image for example without reloading the complete $scope.event to get the updated page.

The other solution which came to my mind but i did not get working so far was to use the nested features of restangular to retrieve all the needed information for my page without having to create a seperate api controller.

$scope.event = Restangular.one("events",'<%= @event.id %>').all("subevents").all("event_images");

I could not find a working solution which would let me iterate over the $scope.event with ng-repeat like i did above and im not sure if this would solve my overall problem.

So the questions i would like to have answered are:

  • Should i stick to the API controller appreaoch or is there a way to make my second idea working to get rid of the api controller ?
  • How do i manipulate the images/subevents without having to reload everything ? ( an example of deleting an image on the page would be great )

Solution

  • This answer your second question using your current API Plunker

    app.js

    var app = angular.module('plunker', []);
    
    app.controller('MainCtrl', function($scope) {
      $scope.name = 'World';
      $scope.event = {
          "id": "54d75dd9686f6c2594020000",
          "subevents": [
              {
                  "id": "54d75de1686f6c2594030000",
                  "name": "Test",
                  "event_images": [
                      {
                          "id": "54df3904686f6c41cf0c0000",
                          "thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
                      },
                      {
                          "id": "54df56c5686f6c41cf850100",
                          "thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
                      }
                  ]
              }
          ]
        };
    });
    app.directive('myEvent', function(){
      return {
        scope: { myEvent: '=' },
        template: "<div>{{myEvent.id}}<div my-subevent='subevent' ng-repeat='subevent in myEvent.subevents'></div></div>",
        controller: function($scope){
          this.removeSubevent = function(e){
            $scope.myEvent.subevents.splice($scope.myEvent.subevents.indexOf(e), 1);
          };
        }
      };
    });
    app.directive('mySubevent', function(){
      return {
        scope: {mySubevent: '='},
        template: "<div>{{  mySubevent.name }} <a href='' ng-click='remove()'>remove subevent</a><div my-subevent-img='img' ng-repeat='img in mySubevent.event_images'></div></div>",
        require: '^myEvent',
        link: function(scope, ele, attrs, myEventCtrl){
          scope.remove = function(){
            myEventCtrl.removeSubevent(scope.subevent);
          };
        },
        controller: function($scope){
          this.removeImg = function(img){
             $scope.mySubevent.event_images.splice($scope.mySubevent.event_images.indexOf(img), 1);
          };
        }
      };
    });
    app.directive('mySubeventImg', function(){
      return {
        scope: { mySubeventImg: '=' },
        template: "<div><img ng-src='mySubeventImg.thumb'><a href ng-click='remove()'>remove img</a></div>",
        require: '^mySubevent',
        link: function(scope, ele, attrs, mySubeventCtrl){
          scope.remove = function(){
            mySubeventCtrl.removeImg(scope.mySubeventImg);
          };
        }
    
      };
    });
    

    index.html

    <div my-event="event"></div>