rails vue.js deep nested (attributes of attribute)

2019-09-02 02:58发布

I created one nested form by watching gorails tutorial It is fine and i done it. Issue started when i want to creat nested model under on other nested model. I have Survey model and it is main model. Then i added Question model and made form with vue.js. So I added Choice model under question ( you can notice in survey controller params) First problem is; i don't know how i can define/implemen in vue.js control.(hello_vue.js) And second importan point is: how i can create form elements in new.html

This is my survey.rb model:

class Survey < ApplicationRecord
    has_many :questions, dependent: :destroy
    accepts_nested_attributes_for :questions, allow_destroy: true
    belongs_to :user
end

and surveys_controller.rb

class SurveysController < ApplicationController
  before_action :set_survey, only: [:show, :edit, :update, :destroy]


    def survey_params
      params.require(:survey).permit(:user_id, :name, questions_attributes:[:id,:survey_id, :title, :qtype, :_destroy, choices_attributes:[:id,:question, :ctext]])
    end
end

This is nested model of Survey : question.rb:

class Question < ApplicationRecord
    enum qtype: [:multiple_choice, :check_boxes, :short_answer]
  belongs_to :survey
  has_many :choices
  accepts_nested_attributes_for :choices, allow_destroy: true
end

So finaly vue.js file:

 import TurbolinksAdapter from 'vue-turbolinks'
 import Vue from 'vue/dist/vue.esm'
 import VueResource from 'vue-resource'

Vue.use(VueResource)
Vue.use(TurbolinksAdapter)

Vue.component('app', App)
 document.addEventListener('turbolinks:load', () => {
    Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
      var element = document.getElementById("survey-form")


if (element != null){
    var survey = JSON.parse(element.dataset.survey)
    var questions_attributes = JSON.parse(element.dataset.questionsAttributes)
    var choices_attributes = JSON.parse(element.dataset.choicesAttributes)
    questions_attributes.forEach(function(question) { question._destroy = null })
    survey.questions_attributes = questions_attributes

    var app = new Vue({

    el: element,
    //mixins: [TurbolinksAdapter],  
    data: function(){
        return { survey: survey }

    },

    methods:{
        addQuestion: function(){

            this.survey.questions_attributes.push({
                id: null,
                title:"",
                qtype:"",
                _destroy: null


            })
        },
       removeQuestion: function(index) {
          var question = this.survey.questions_attributes[index]

          if (question.id == null) {
            this.survey.questions_attributes.splice(index, 1)
          } else {
            this.survey.questions_attributes[index]._destroy = "1"
          }
        },
        undoRemove: function(index) {
          this.survey.questions_attributes[index]._destroy = null
        },

                saveSurvey: function() {
          // Create a new survey
          if (this.survey.id == null) {
            this.$http.post('/surveys', { survey: this.survey }).then(response => {
              Turbolinks.visit(`/surveys/${response.body.id}`)
            }, response => {
              console.log(response)
            })




          // Edit an existing survey
          } else {
            this.$http.put(`/surveys/${this.survey.id}`, { survey: this.survey }).then(response => {
              Turbolinks.visit(`/surveys/${response.body.id}`)
            }, response => {
              console.log(response)
            })
          }
        },


        existingSurvey: function() {
          return this.survey.id != null
        }

    }   
   })
    }


 })

_form.html.erb

<%= content_tag :div,
    id: "survey-form",
    data: {
      survey: survey.to_json(except: [:created_at, :updated_at]),
      questions_attributes: survey.questions.to_json,

    } do %>

<label>Survey Name</label>
<input qtype="text" v-model="survey.name">

<h4>Questions</h4>
<div v-for="(question, index) in survey.questions_attributes">
    <div v-if="question._destroy == '1'">
      {{ question.title }} will be removed. <button v-on:click="undoRemove(index)">Undo</button>
    </div>
    <div v-else>
      <label>Question</label>

      <input qtype="text" v-model="question.title" />

     <label>Qestion qtype</label>
  <select v-model="question.qtype">
  <option v-for="qtype in <%= Question.qtypes.keys.to_json %>"
    :value=qtype>
    {{ qtype }}
  </option>
</select>


      <button v-on:click="removeQuestion(index)">Remove</button>
    </div>

    <hr />
  </div>

  <button v-on:click="addQuestion">Add Question</button>

<br>

<button v-on:click="saveSurvey" >Save Survey</button>


    <% end %>

1条回答
该账号已被封号
2楼-- · 2019-09-02 03:18

I followed this same tutorial and started running into issues using JSON.parse with more complex nested attributes. Try using Jbuilder to build your JSON objects and look into the gon gem to pass your Rails variables into Javascript. It'll be much easier to query your database and pass the results into your Javascript file using the nested naming that Rails needs. For example...

survey = @survey

json.id survey.id

json.survey do
  json.(survey, :user_id, :name)  
  json.questions_attributes survey.questions do |question|
    json.(question, :id, :title, :qtype, :_destroy)
    json.choices_attributes question.choices do |choice|
      json.(choice, :id, :ctext)
    end
  end
end

It allows you to do things like...

var survey = gon.survey

Instead of...

var survey = JSON.parse(element.dataset.survey)

And you can pass gon.jbuilder from your controller action and have your defined JSON object ready and available in Vue.

查看更多
登录 后发表回答