3

I have two objects, Events & Comments:

   {
    "name": "events",
    "fields": {
      "name": {
        "type": "string"
      },
      "date": {
        "type": "datetime"
      },
      "time": {
        "type": "datetime"
      },
      "info": {
        "type": "text"
      },
      "users": {
        "collection": "users_events",
        "via": "event"
      },
      "eventCommentsId": {
        "collection": "comments",
        "via": "eventId"
      },
    }
  },
  {
    "name": "comments",
    "fields": {
      "content": {
        "type": "text"
      },
      "owner": {
        "object": "users"
      },
      "eventId": {
        "object": "events"
      },
      "date": {
        "type": "datetime"
      }
    }
  }

Each event should have its own unique collection of comments. So, its a One to Many relationship.

Right now, I can just get all of the comments instead of only the ones that correspond to each event. My thinking is that I need to include the id of the event in each comment. But, I'm not entirely sure how to do that.

If anyone could help me out with this, that would be amazing!

I'm building the app with Ionic/AngularJS and I'm storing my data with Backand.

Thanks in advance!

.controller('EventDetailCtrl', ['$scope', '$stateParams', '$ionicSideMenuDelegate', 'EventService', 'CommentService',function($scope, $stateParams, $ionicSideMenuDelegate, EventService, CommentService) { 
  $scope.openMenu = function () {
    $ionicSideMenuDelegate.toggleLeft();
  };    

  var id = $stateParams.id;
  EventService.getEvent(id).then(function(response){
   $scope.event = response.data;
});

  $scope.comments = [];
  $scope.input = {};

  function getAllComments() {
    CommentService.getComments()
    .then(function (result) {
      $scope.comments = result.data.data;
    });
  }

  $scope.addComment = function() {
    CommentService.addComment($scope.input)
    .then(function(result) {
      $scope.input = {};
      getAllComments();
    });
  }

  $scope.deleteComment = function(id) {
    CommentService.deleteComment(id)
    .then(function (result) {
      getAllComments();
    });
  }

  getAllComments();

}])

.service('CommentService', function ($http, Backand) {    
  var baseUrl = '/1/objects/';
  var objectName = 'comments/';

  function getUrl() {
    return Backand.getApiUrl() + baseUrl + objectName;
  }

  function getUrlForId(id) {
    return getUrl() + id;
  }

  getComments = function () {
    return $http.get(getUrl());
  };

  addComment = function(event) {
    return $http.post(getUrl(), event);
  }

  deleteComment = function (id) {
    return $http.delete(getUrlForId(id));
  };

  getComment = function (id) {
    return $http.get(getUrlForId(id));
  };


  return {
    getComments: getComments,
    addComment: addComment,
    deleteComment: deleteComment,
    getComment: getComment
  }   
})

3 Answers3

2

It is better to move the logic of getting all comments of a specific event from the backend rather than getting everything from server and filtering on front-end. You can make the call such as:

http://mysite/getComments?eventId=2533213

And in your comments schema (for DB), you can have one field as eventId. You can then fetch all the comments having the event Id provided in the call from DB and return to your app in response.

In front-end you can do :

Controller:

function getCommentsById(event) {
    CommentService.getCommentsById(event)
    .then(function (result) {
      $scope.comments = result.data.data;
    });
  }

Service:

getCommentsById = function (event) {
    return $http.get(getUrl() + "?eventId=" + event.id); //supposing you have event.id
  };
Muhammad Ahsan Ayaz
  • 1,867
  • 10
  • 12
2

You could also create a query in the dashboard of Backand and name it like GetCommentsByEventId and with the api endpoint Backand.getApiUrl() + '/1/query/data/GetCommentsByEventId' you can pass the id and you'll get the comments for that event. (see screenshot below) screenshot of query

In the query with the SQL SELECT * FROM comments WHERE event='{{eventId}}' you'll get the comments for that event id.

Or you could use a filter in the API url like Backand.getApiUrl() + /1/comments?filter={"fieldName":"event","operator":"in", "value": "33"} (not url encoded here for readability, 33 is the eventId)

My DB model for the demo looks like in this screenshot: screenshot db model

And the DB model json is like following:

    [
{
    "name": "users",
    "fields": {
        "email": {
            "type": "string"
        },
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        },
        "user": {
            "collection": "comments",
            "via": "user"
        }
    }
},
{
    "name": "events",
    "fields": {
        "title": {
            "type": "string"
        },
        "created_at": {
            "type": "datetime"
        },
        "eventId": {
            "collection": "comments",
            "via": "event"
        }
    }
},
{
    "name": "comments",
    "fields": {
        "event": {
            "object": "events"
        },
        "user": {
            "object": "users"
        },
        "text": {
            "type": "string"
        }
    }
}
]

In the DB model I would only rename the eventId in events model to eventCommentsId to make it clear that it's a reference to the comments.

Please find the demo code below (not working here, insecure operation in Backand script) or the working demo in this jsfiddle.

The angular code can be refactored a bit but it is working. If something else is not correct please let me know because this is my first example with Backand.

Also there's no user login in the demo that's why everything will be added with userId = 1.

angular.module('demoApp', ['ionic', 'backand'])
  //Update Angular configuration section
  .config(function(BackandProvider) {
    BackandProvider.setAppName('myfirstapp123');
    //BackandProvider.setSignUpToken('-token-');
    BackandProvider.setAnonymousToken('9f99054f-3205-426b-afc7-158d7ac3500f');
  })
  .controller('mainController', MainController)
  .service('dataService', dataService)
  .factory('commentsFactory', Comments);

function MainController($scope, $http, Backand, dataService, commentsFactory) {
  var vm = this,
    comment = commentsFactory;

  vm.currentUserId = 1; //<<<< should be the current user later (no login yet)

  vm.displayedComments = {};
  dataService.getList('events').then(function(response) {
    vm.events = response.data;
  }); //eventsFactory;

 vm.addEvent = function(newTitle) {
   
    if (!newTitle) return; // don't add empty events
   // this should be in a factory later
    var newDate = new Date();
    
    return $http({
        method: 'POST',
        url: Backand.getApiUrl() + '/1/objects/events?returnObject=true',
        data: {
          title: newTitle,
          created_at: newDate
        }
      }).then(function(response) {
       //console.log(response, vm.events);
       vm.events.data.push(response.data);
      });
  }
  vm.addComment = function(userId, event, text) {
    //event.comments.push(
    if (!text) return;
    
    comment.create(userId, event.id, text).then(function(response) {
      event.comments.push(response.data);
      //console.log(response);
    });
  }

  vm.getComments = commentsFactory.getComments;
  vm.showComment = function(event) {
    //console.log(event);
    commentsFactory.getComments(event.id).then(function(response) {
      vm.displayedComments[event.id] = !vm.displayedComments[event.id];
      event.comments = response.data;
    });
  };
  vm.remove = function(event, commentId) {
   console.log('removing', event, commentId);
   commentsFactory.delete(commentId).then(function(response){
     //console.log('removed', response, event);
      // next update collection in angular
      event.comments = event.comments.filter(function(comment) {
       return comment.id !== commentId; // remove comment
      });
    });
  }
}

function dataService($http, Backand) {
  var vm = this;
  //get the object name and optional parameters
  vm.getList = function(name, sort, filter) {
    return $http({
      method: 'GET',
      url: Backand.getApiUrl() + '/1/objects/' + name,
      params: {
        pageSize: 20,
        pageNumber: 1,
        filter: filter || '',
        sort: sort || ''
      }
    });
  }
}

function Comments($http, Backand) {
  return {
    create: function(user, event, text) {
      return $http({
        method: 'POST',
        url: Backand.getApiUrl() + '/1/objects/comments?returnObject=true',
        data: {
          event: event,
          user: user,
          text: text
        }
      });
    },
    delete: function(commentId) {
     return $http({
        method: 'DELETE',
        url: Backand.getApiUrl() + '/1/objects/comments/' + commentId
      });
    },
    getComments: function(id) {
      return $http({
        method: 'GET',
        url: Backand.getApiUrl() + '/1/query/data/GetCommentsByEventId',
        params: {
          parameters: {
            eventId: id
          }
        }
      });
    }
  }
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/ionic/1.2.4/css/ionic.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ionic/1.2.4/js/ionic.bundle.js"></script>
<script src="https://cdn.backand.net/backand/dist/1.8.2/backand.min.js"></script>

<div ng-app="demoApp" ng-controller="mainController as mainCtrl">

  <ion-header-bar align-title="center" class="bar-dark">
    <h1 class="title">EventsApp</h1>
   </ion-header-bar>
  <ion-content>
    <!--<pre>{{mainCtrl.events | json : 2}}</pre-->
    <!--<pre>{{mainCtrl.displayedComments|json:2}}</pre>-->
    <ion-list>
      <ion-item>
      <form ng-submit="mainCtrl.addEvent(mainCtrl.newEventTitle); mainCtrl.newEventTitle = '';">
        <input type="text" ng-model="mainCtrl.newEventTitle" placeholder="event title..."/>
        <button class="item item-icon-left">
          <i class="icon ion-plus-round"></i>
          add event
        </button>
      </form>
      </ion-item>
      <ion-item ng-if="mainCtrl.events.data.length === 0">
        <h1>
        There are no events yet.
        </h1>
      </ion-item>
      <ion-item ng-repeat="event in mainCtrl.events.data | orderBy: '-created_at'" class="item item-button-right">
        <h1>
          {{event.title}}
        </h1>
        <button ng-click="mainCtrl.showComment(event)" class="button icon-left ion-ios-chatbubble" title="{{mainCtrl.displayedComments[event.id]? 'hide comments': 'show comments'}}">
        </button>
        <div ng-show="mainCtrl.displayedComments[event.id]" class="list">
          
          <form ng-submit="mainCtrl.addComment(mainCtrl.currentUserId, event, commentText)">
            <label class="item item-input">
              <span class="input-label">Username</span>
              <input type="text" disabled ng-model="mainCtrl.currentUserId">
            </label>
            <label class="item item-input">
              <span class="input-label">Comment</span>
              <input type="text" ng-model="commentText">
            </label>
            <button class="button button-positive">
               leave comment
            </button>
          </form>
          
          <ion-list>
            <ion-item ng-if="event.comments.length === 0">
              <h2>
              No comment yet. Be the first and leave a comment.
              </h2>
            </ion-item>
            <ion-item ng-repeat="comment in event.comments">
            <div class="item item-button-right">
            user {{comment.user}} wrote {{comment.text}}
              <button ng-click="mainCtrl.remove(event, comment.id)" class="button button-positive">
              <i class="icon ion-ios-trash"></i>
              </button>
              </div>
            
            </ion-item>
          </ion-list>
        </div>
      </ion-item>
    </ion-list>
  </ion-content>
</div>
AWolf
  • 8,770
  • 5
  • 33
  • 39
2

You can also use the ?deep=true query parameter which brings all the collection in case of one to many.

In you example /1/objects/event/1?deep=true will return all the comments for event with id=1.

var id = $stateParams.id;
var useDeep = true;
EventService.getEvent(id, useDeep).then(function(response){
  $scope.event = response.data;
  $scope.comments = response.data.comments;
});
Itay
  • 734
  • 4
  • 5