AngularJS - ngFileUpload 具有 Rails 的嵌套属性

AngularJS - ngFileUpload with nested attributes for Rails

我有点进退两难。我正在使用 Rails 4.2.3 和 AngularJS。我有一个表单需要为关联的数据模型上传嵌套属性并上传图像。正如我所发现的那样,使用 AJAX 请求上传文件并不是一件容易的事,我正在使用 ng-file-upload 来解决这个问题,但是我遇到了一些基本的使用问题。

我的Rails模特:

class Job < ActiveRecord::Base
  has_many :references, dependent: :destroy
  accepts_nested_attributes_for :references, limit: 5
end

class Reference < ActiveRecord::Base
  belongs_to :job
end

Rails 提交表单时需要此格式:

Parameters: {"job"=>{"title"=>"Example Title", "company"=>"Example Company", "description"=>"Example Description", "references_attributes"=>{"0"=>{"reference"=>"Example Reference", "id"=>"1"}}, "image"=> #plus the image data here}

使用 ng-file-upload,似乎几乎不可能包含嵌套属性(或者也许我遗漏了一些东西,我已经不止一次阅读文档以试图找到任何关于此的内容)。我采用了这种有点古怪的方法,以 Rails 可以理解的方式包含嵌套属性。表格如下:

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <form ng-submit="save(job)" accept-charset="UTF-8" enctype="multipart/form-data">
      <label for="job_title">Title</label>
      <input ng-model="job.title" 
             class="form-control" 
             id="job_title" 
             name="job[title]" 
             type="text" 
         required />
  <div ng-repeat="error in errors.title" class="alert alert-danger">Title {{error}}</div>

  <label for="job_company">Company</label>
  <input ng-model="job.company" 
         class="form-control" 
         id="job_company" 
         name="job[company]" 
         type="text" 
         required />
  <div ng-repeat="error in errors.company" class="alert alert-danger">Company {{error}}</div>

  <label for="job_years">Years</label>
  <input ng-model="job.years" 
         class="form-control" 
         id="job_years" 
         name="job[years]" 
         type="text" />
  <div ng-repeat="error in errors.years" class="alert alert-danger">Years {{error}}</div>

  <label for="job_manager">Manager</label>
  <input ng-model="job.manager" 
         class="form-control" 
         id="job_manager" 
         name="job[manager]" 
         type="text" />
  <div ng-repeat="error in errors.manager" class="alert alert-danger">Manager {{error}}</div>

  <label for="job_contact">Contact</label>
  <input ng-model="job.contact" 
         class="form-control" 
         id="job_contact" 
         name="job[contact]" 
         type="text" />
  <div ng-repeat="error in errors.contact" class="alert alert-danger">Contact {{error}}</div>

  <label for="job_address">Address</label>
  <input ng-model="job.address" 
         class="form-control" 
         id="job_address" 
         name="job[address]" 
         type="text" />
  <div ng-repeat="error in errors.address" class="alert alert-danger">Address {{error}}</div>

  <label for="job_description">Description</label>
  <textarea ng-model="job.description" 
            class="form-control" 
            id="job_description" 
            name="job[description]" 
            required>
  </textarea>
  <div ng-repeat="error in errors.description" class="alert alert-danger">Description {{error}}</div>

  <label for="job_skills">Skills</label>
  <input ng-model="job.skills" 
         class="form-control" 
         id="job_skills" 
         name="job[skills]" 
         type="text" />
  <div ng-repeat="error in errors.skills" class="alert alert-danger">Skills {{error}}</div>

  <label for="job_references">References</label>

  <input ng-model="job.references[0]" 
         class="form-control" 
         id="job_references_attributes_0_reference" 
         name="job[references_attributes][0][reference]" 
         type="text" />

  <input ng-model="job.references[1]"  
         class="form-control" 
         id="job_references_attributes_1_reference" 
         name="job[references_attributes][1][reference]" 
         type="text" />

  <input ng-model="job.references[2]" 
         class="form-control" 
         id="job_references_attributes_2_reference" 
         name="job[references_attributes][2][reference]" 
         type="text" />

  <input ng-model="job.references[3]" 
         class="form-control" 
         id="job_references_attributes_3_reference" 
         name="job[references_attributes][3][reference]" 
         type="text" />

  <input ng-model="job.references[4]" 
         class="form-control" 
         id="job_references_attributes_4_reference" 
         name="job[references_attributes][4][reference]" 
         type="text" />

  <label for="job_image">Image</label>
  <input ng-model="job.image" 
         class="width-100" 
         id="job_image" 
         name="job[image]" 
         type="file"
         ngf-select
         accept="image/*" 
         ngf-max-size="5MB" />
  <div ng-repeat="error in errors.image" class="alert alert-danger">Image {{error}}</div>

  <div class="center">
    <div class="btn-group">  
      <input class="btn btn-large btn-primary" 
                 name="commit" 
                 type="submit" 
                 value="Submit" />
        </div>
        <div class="btn-group">
          <a ng-click="back()" href class="btn btn-large btn-default">&larr; Cancel</a>
        </div>
      </div>
    </form>  
  </div>
</div>

我指的 "hacky" 位就是我要 "job.references[0]" 进行的地方。以下是我如何使用 ng-file-upload 来包含嵌套属性:

 $scope.save = function(job) {
  var file = job.image;
  Upload.upload({
    url: '/api/jobs/' + job.id,
    method: 'PUT',
    fields: {
      'job[title]': job.title,
      'job[company]': job.company,
      'job[description]': job.description,
      'job[years]': job.years,
      'job[manager]': job.manager,
      'job[contact]': job.contact,
      'job[skills]': job.skills,
      'job[address]': job.address,
      'job[references_attributes][0][reference]': job.references[0],
      'job[references_attributes][1][reference]': job.references[1],
      'job[references_attributes][2][reference]': job.references[2],
      'job[references_attributes][3][reference]': job.references[3],
      'job[references_attributes][4][reference]': job.references[4] },
    file: file,
    fileFormDataName: 'job[image]'
  }).progress(function (evt) {
    $scope.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
    console.log('Progress: ' + $scope.progress + '% ' + evt.config.file.name);
  }).success(function (data, status, headers, config) {
    console.log('File ' + config.file.name + 'uploaded. Response: ' + data);
    $scope.jobs.push(data);
    $state.go('jobs.show', {id: $stateParams.id})
  }).error(function (data, status, headers, config) {
    console.log('Error status: ' + status);
    $scope.errors = data;
  });
}

这适用于新模型条目,但是当我编辑现有条目时,这里出现了问题...这是使用此设置发送到 Rails 的 JSON 数据:

Parameters: {"job"=>{"title"=>"Example Title", "company"=>"Example Company", "description"=>"Example Description", "references_attributes"=>{"0"=>{"reference"=>"Example Reference"}}, "image"=> #plus the image data here}

您可能已经注意到,"id" 属性不再包含在嵌套属性中。这导致 Rails 在我提交任何编辑时复制现有的嵌套属性。有没有人更好地了解如何将嵌套属性包含在 ng-file-upload 中?或者甚至没有 ng-file-upload 的解决方案?

提前致谢!

最近对该工具进行了更新,极大地帮助了嵌套属性。为此使用 Rails,嵌套属性... 在表单中,您可以使用以下格式:

<input ng-model="Object.nested_attributes[0]" />

然后在 AngularJS 控制器中:

     Upload.upload({
      url: 'RailsController#create or update path',
      method: 'post or put',
      data: {
        YourDataModel: {
          image: file,
          nested_attributes: $scope.Object.nested_attributes
        }
      }
    });