سؤال

لدي تطبيق Rails الذي ينص على المنزل. house يحتوي على rooms والغرف لها سمات متداخلة ل light و small_appliance. انا املك calculator وحدة التحكم ، وهي الطريقة التي سيصل بها المستخدمون النهائيون إلى التطبيق.

مشكلتي هي أنه لا يمكنني الحصول على الجزئي للإضافة rooms لتقديم وتقديم بشكل صحيح من calculator. تتيح الصفحة الأولية للمستخدم إدخال house المعلومات التي يتم حفظها باستخدام save_house عند النقر فوق الإرسال. هذا أيضًا يعيد توجيه المستخدم إلى add_rooms الصفحة ، حيث يمكنهم إضافة غرف إلى المنزل.

add_rooms يعرض بشكل صحيح ، ولكن عندما انقر فوق إرسال ، أحصل على هذا الخطأ:

RuntimeError in Calculator#add_room

Showing app/views/calculator/add_rooms.html.erb where line #2 raised:

Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id

Extracted source (around line #2):

1: <div id="addRooms">
2:   <p>House id is <%= @house.id %></p>
3:   
4:   <h3>Your rooms:</h3>
5:   <% if @house.rooms %>

RAILS_ROOT: C:/Users/ryan/Downloads/react
Application Trace | Framework Trace | Full Trace

C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb:2:in `_run_erb_app47views47calculator47add_rooms46html46erb'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:36:in `add_room'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:33:in `add_room'

هذا غريب بالنسبة لي ، لأنه عندما add_rooms أول عرض ، يوضح house_id. لا أفهم لماذا لم يتم تمريره بعد تقديم النموذج.

هذا هو الرمز:

التطبيق/النماذج/الغرفة.

class Room < ActiveRecord::Base
  # schema { name:string, house_id:integer }
  belongs_to :house
  has_many :lights, :dependent => :destroy
  has_many :small_appliances, :dependent => :destroy
  validates_presence_of :name
  accepts_nested_attributes_for :lights, :reject_if => lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => true
  accepts_nested_attributes_for :small_appliances, :reject_if => lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => true         
end

التطبيق/النماذج/house.rb

class House < ActiveRecord::Base
  has_many :rooms

  # validation code not included

  def add_room(room)
    rooms << room
  end

end

التطبيق/وحدات التحكم/calculator_controller.rb

class CalculatorController < ApplicationController
  def index
  end

  def save_house
    @house = House.new(params[:house])
    respond_to do |format|
      if @house.save
        format.html { render :action => 'add_rooms', :id => @house }
        format.xml { render :xml => @house, :status => :created, :location => @house }
      else
        format.html { render :action => 'index' }
        format.xml  { render :xml => @house.errors, :status => :unprocessable_entity }
      end
    end
  end

  def add_rooms
    @house = House.find(params[:id])
    @rooms = Room.find_by_house_id(@house.id)

  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before adding rooms"
    redirect_to :action => 'index'
  end

  def add_room
    @room = Room.new(params[:room])
    @house = @room.house

    respond_to do |format|
      if @room.save
        flash[:notice] = "Room \"#...@room.name}\" was successfully added."
        format.html { render :action => 'add_rooms' }
        format.xml { render :xml => @room, :status => :created, :location => @room }
      else
        format.html { render :action => 'add_rooms' }
        format.xml  { render :xml => @room.errors, :status => :unprocessable_entity }
      end
    end
  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before adding a room"
    redirect_to :action => 'index'
  end

  def report
    flash[:notice] = nil
    @house = House.find(params[:id])
    @rooms = Room.find_by_house_id(@house.id)
  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before generating a report"
    redirect_to :action => 'index'
  end

end

تطبيق/طرق عرض/حاسبة/add_rooms.html.erb

<div id="addRooms">
  <p>House id is <%= @house.id %></p>

  <h3>Your rooms:</h3>
  <% if @house.rooms %>
  <ul>
    <% for room in @house.rooms %>
    <li>
      <%= h room.name %> has <%= h room.number_of_bulbs %> 
      <%= h room.wattage_of_bulbs %> watt bulbs, in use for 
      <%= h room.usage_hours %> hours per day.
    </li> 
    <% end %>
  </ul>
  <% else %>
  <p>You have not added any rooms yet</p>
  <% end %>  

  <%= render :partial => 'rooms/room_form' %>

  <br />
</div>

<%= button_to "Continue to report", :action => "report", :id => @house %>

التطبيق/المناظر/الغرف/_room_form.html.erb

<% form_for :room, @house.rooms.build, :url => { :action => :add_room } do |form| %>
  <%= form.error_messages %>
  <p>
    <%= form.label :name %><br />
    <%= form.text_field :name %>
  </p>

  <h3>Lights</h3>
  <% form.object.lights.build if form.object.lights.empty? %>
  <% form.fields_for :lights do |light_form| %>
    <%= render :partial => "light", :locals => { :form => light_form } %>
  <% end %>
  <p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p>

  <h3>Small Appliances</h3>
  <% form.object.small_appliances.build if form.object.small_appliances.empty? %>
  <% form.fields_for :small_appliances do |sm_appl_form| %>
    <%= render :partial => "small_appliance", :locals => { :form => sm_appl_form } %>
  <% end %>
  <p class="addLink"><%= add_child_link "[+] Add new small appliance", form, :small_appliances %></p>

  <p><%= form.submit "Submit" %></p>
<% end %>

application_helper.rb

module ApplicationHelper
  def remove_child_link(name, form)
    form.hidden_field(:_delete) + link_to_function(name, "remove_fields(this)")
  end

  def add_child_link(name, form, method)
    fields = new_child_fields(form, method)
    link_to_function(name, h("insert_fields(this, \"#{method}\", \"#{escape_javascript(fields)}\")"))
  end

  def new_child_fields(form_builder, method, options = {})
    options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
    options[:partial] ||= method.to_s.singularize
    options[:form_builder_local] ||= :form
    form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |form|
      render(:partial => options[:partial], :locals => { options[:form_builder_local] => form })
    end
  end
end

شكرًا،
ريان

هل كانت مفيدة؟

المحلول

بدافع الفضول لماذا لا تقبل المنزل سمات متداخلة للغرف. هذا من شأنه أن يجعل رمز وحدة التحكم الخاص بك أكثر بساطة ، حيث أن إضافة العديد من الغرف والأضواء والأجهزة الصغيرة بسيطة مثل مجرد القيام @house.update_attributes (params [: House]). ومع ذلك ، فإن هذه ليست إجابة تساعد ، حيث لا يزال لديك مشاكلك الحالية إذا قمت بالتغيير.

خطأك الأول يأتي من السطر الأول من التطبيق/المشاهدات/الآلة الحاسبة/_room_form.html.erb

<% form_for :room, :url => { :action => :add_room, :id => @house } do |form| %>

أنت لا تعطي form_for كائنًا ، لذا فإن طريقة new_child_fields تسمى بواسطة add_child _link تحاول استدعاء RESPRESS_ON_ASSOCIATION على فئة NIL.

الحل هو تغيير الخط إلى

<% form_for :room, @house.rooms.build, :url => { :action => :add_room } do |form| %>

يتيح لك ذلك تبسيط وحدة التحكم الخاصة بك ، لأن غرفة مرتبطة بالمنزل يتم نقلها بالفعل إليها.

def add_room
    @room = Room.new(params[:room])
    @house = @room.house
    respond_to do |format|
      if @room.save
        flash[:notice] = "Room \"#...@room.name}\" was successfully added."
        format.html { render :action => 'add_rooms' }
        format.xml { render :xml => @room, :status => :created, :location => @room }
      else
        format.html { render :action => 'add_rooms' }
        format.xml  { render :xml => @room.errors, :status => :unprocessable_entity }
      end
    end
  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before adding a room"
    redirect_to :action => 'index'
  end

أعتقد أن خطأك الثاني هو نفس المشكلة. ومع ذلك ، نظرًا لأنك تتصل بـ As_Many Accessor بدلاً من إنشاء NIL ، فأنت تمرر صفيفًا فارغًا ، وهو ما يفسر الفرق في رسائل الخطأ. مرة أخرى ، يتمثل الحل في بناء جهاز ضوئي وصغير قبل تقديمه إذا لم يكن هناك شيء حتى الآن.

  <h3>Lights</h3>
  <% form.object.lights.build if form.object.lights.empty? %>
  <% form.fields_for :lights do |light_form| %>
    <%= render :partial => 'rooms/light', :locals => { :form => light_form } %>
  <% end %>
  <p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p>

  <h3>Small Appliances</h3>
  <% form.object.small_appliances.build if form.object.small_appliances.empty? %>
  <% form.fields_for :small_appliances do |sm_appl_form| %>
    <%= render :partial => 'rooms/small_appliance', :locals => { :form => sm_appl_form } %>
  <% end %>

خطأك الجديد يأتي من هذا:

 def new_child_fields(form_builder, method, options = {})
    options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new

    # specifically this line. 
    options[:partial] ||= method.to_s.singularize

    options[:form_builder_local] ||= :form
    form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |form|
      render(:partial => options[:partial], :locals => { options[:form_builder_local] => form })
    end
  end

يفترض new_child_fields أن الجزئي _light موجود في مجلد التطبيق/المشاهدات/الحاسبة

يتمثل الحل إما في تحريك الضوء و small_appliances إلى هذا المجلد أو تعديل أساليب المساعد لقبول خيار جزئي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top