0
votes

I have a loopback API backend with a storage service which stores the images and form data I send. Testing the API on its own everything works so it it obvious something is missing in my logic in my controller. I use angular-file-upload & code from this https://github.com/strongloop/loopback-component-storage/tree/master/example-2.0

So I'm basically trying to post a form which first drops an image in a container on the API server, then when I click submit I'm trying to pull image name from the object the uploader created and post it along the other form inputs.

When I submit with the form, on the console I get a Network 422 error on the Image property - can't be blank. The DB takes a string for this property.

Controller

app.controller('exampleCtrl', function($scope, $http, $resource, clientsUrl, $fileUploader, $window) {

//Code for uploading files to API - NEED TO WORK ON IT
'use strict';

// create a uploader with options
var uploader = $scope.uploader = $fileUploader.create({
  scope: $scope,                          // to automatically update the html. Default: $rootScope
  url: 'http://www.example.com:3000/api/containers/container1/upload',
  formData: [
    { key: 'value' }
  ],
  filters: [
    function (item) {                    // first user filter
      console.info('filter1');
      return true;
    }
  ]
});

// ADDING FILTERS

uploader.filters.push(function (item) { // second user filter
  console.info('filter2');
  return true;
});

// REGISTER HANDLERS

uploader.bind('afteraddingfile', function (event, item) {
  console.info('After adding a file', item);
});

uploader.bind('whenaddingfilefailed', function (event, item) {
  console.info('When adding a file failed', item);
});

uploader.bind('afteraddingall', function (event, items) {
  console.info('After adding all files', items);
});

uploader.bind('beforeupload', function (event, item) {
  console.info('Before upload', item);
});

uploader.bind('progress', function (event, item, progress) {
  console.info('Progress: ' + progress, item);
});

uploader.bind('success', function (event, xhr, item, response) {
  console.info('Success', xhr, item, response);
  $scope.$broadcast('uploadCompleted', item);
});

uploader.bind('cancel', function (event, xhr, item) {
  console.info('Cancel', xhr, item);
});

uploader.bind('error', function (event, xhr, item, response) {
  console.info('Error', xhr, item, response);
});

uploader.bind('complete', function (event, xhr, item, response) {
  console.info('Complete', xhr, item, response);
});

uploader.bind('progressall', function (event, progress) {
  console.info('Total progress: ' + progress);
});

uploader.bind('completeall', function (event, items) {
  console.info('Complete all', items);
});


$scope.load = function () {
  $http.get('http://www.example.com:3000/api/containers/container1/files').success(function (data) {
    console.log(data);
    $scope.files = data;
  });
};

$scope.delete = function (index, id) {
  $http.delete('http://www.example.com:3000/api/containers/container1/files/' + encodeURIComponent(id)).success(function (data, status, headers) {
    $scope.files.splice(index, 1);
  });
};

$scope.$on('uploadCompleted', function(event) {
  console.log('uploadCompleted event received');
  $scope.load();
});


    $scope.clientsItemsResource = $resource(clientsUrl + ":id", {id: "@id"},
            { create : { method: "POST"}, save: { method: "PUT"}}
        );

//Creates prototype for setting image on scope
function SetImageScope($scope, $window){
    $scope.image = 'Superhero';

    SetImageScope.prototype.$scope = $scope;
};

    //Sets the newClientsItem
    SetImageScope.prototype.setFile = function(element) {
        var $scope = this.$scope;
        $scope.$apply(function(){
            $scope.newClientsItem = element.files[0];
        });
    };

    $scope.createClientsItem = function (clientsItem) {

        //creates the new item
  new $scope.clientsItemsResource(clientsItem).$create().then(function (newClientsItem) {
     $scope.clientsItems.push(newClientsItem);
   });
};
});

HTML

<div ng-controller="exampleCtrl" ng-file-drop="ng-file-drop" class="container">
<div class="row-fluid">
<div class="col-md-12"><br/></div>
<form>
  <div class="form-group">
    <label>Title</label>
    <input type="text" ng-model="newClientsItem.title" required="required" class="form-control"/><br/>
    <label>Link</label>
    <input type="url" ng-model="newClientsItem.link" required="required" class="form-control"/><br/>
    <label>Image</label>
    <input ng-file-select="ng-file-select" type="file" onchange="SetImageScope.prototype.setFile(this)" class="btn btn-primary"/>
    <table class="table">
      <thead>
        <tr>
          <th width="50%">Image</th>
          <th ng-show="uploader.isHTML5">Size</th>
          <th ng-show="uploader.isHTML5">Progress</th>
          <th>Status</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="item in uploader.queue">
          <td><strong>{{ item.file.name }}</strong></td>
          <td ng-show="uploader.isHTML5" nowrap="">
            {{
            item.file.size/1024/1024|number:2 }} MB
          </td>
          <td ng-show="uploader.isHTML5">
            <div style="margin-bottom: 0;" class="progress">
              <div role="progressbar" ng-style="{ &quot;width&quot;: item.progress + &quot;%&quot; }" class="progress-bar"></div>
            </div>
          </td>
          <td class="text-center"><span ng-show="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span><span ng-show="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span><span ng-show="item.isError"><i class="glyphicon glyphicon-remove"></i></span></td>
          <td nowrap="">
            <button type="button" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess" class="btn btn-success btn-xs"><span class="glyphicon glyphicon-upload"></span>Upload</button>
            <button type="button" ng-click="item.cancel()" ng-disabled="!item.isUploading" class="btn btn-warning btn-xs"><span class="glyphicon glyphicon-ban-circle"></span>Cancel</button>
            <button type="button" ng-click="item.remove()" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></span>Remove</button>
          </td>
        </tr>
      </tbody>
    </table><a ng-click="createClientsItem(newClientsItem)" class="btn btn-default btn-lg">Ajouter</a>
  </div>
</form>

2
When I had your problem this article helped me out: uncorkedstudios.com/blog/… - simeg
thanks for the tip and I've actually at this article while I was building all this...but something is missing somewhere in my logic that I'm not seeing after working on this code for a while. Can you see anything I'm not? - Emmanuel Henri
Perhaps the lack of the headers and transformRequest. It's hard to tell since I think you're using a quite different approach than in the article. Are you sure the DB is taking a simple string and not a file (containing some other stuff)? - simeg
Yes I am certain since both elements are uploaded in different areas of the API. Where I think my code might be missing something is where I pull the name generated from image object (around SetImageScope function) and then assign it to the newClientsItem object generated from the form. - Emmanuel Henri

2 Answers

0
votes

422 status code means that model validation wasn't passed. Try to inspect sent data on client and server side. Debugging with model hooks and Loopback debug strings should help.

0
votes

Not sure if my solution are correct. But you can consider them:

  1. If you want to make the file-submitting separate to the form-submitting, let take a try on moving the file <input> tag out of the <form> tag.

  2. There are no places in your code setting the 'multipart' data type for the uploading. That may cause the problem.