0

Смотрите мою проблему:Rails accepts_nested_attributes_for + HABM возвращает массив с пустой строкой

class MedicalRecord < ActiveRecord::Base 
    has_many :evaluations, dependent: :destroy 

    accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :all_blank 
end 

class Evaluation < ActiveRecord::Base 
    belongs_to :medical_record 

    has_and_belongs_to_many :edemas 

    validates :description, presence: true 
end 

Моей форма показывающую выбор полей с множественным атрибутом.

<%= form_for @medical_record do |f| %> 
    <%= f.fields_for :evaluations do |e| %> 
    <%= e.text_field :description %> 

    <%= e.collection_select :edema_ids, Edema.all.order(:title), :id, :title, 
     { }, multiple: true %> 
    <% end %>  
<% end %> 

Когда я не выбираю «отек», форма отправляет массив с одной пустой строкой. Итак, reject_if возвращает false, и мне нужно заполнить поле описания. В этом случае reject_if должен вернуть true.

Что я могу сделать для этого, чтобы работать?

Большое спасибо

+0

Можете ли вы включить HTML-код, который * в настоящее время * (в «сломанном» состоянии) отображается для формы? – jasonmklug

ответ

1

Если ваша цель состоит в том, чтобы не иметь пустую строку в вашем массиве :edema_ids, вы можете пройти include_hidden: false до collection_select.

Это может вызвать проблемы, если вы в конечном итоге захотите «отменить выбор» всех отеков для существующей оценки, поскольку браузер не отправит массив :edema_ids вообще, если его значение действительно пусто (поскольку это множественный выбор) , примерно по той же причине он не отправил бы не установленное значение флажка. Включение пустой строки в пустой массив - это способ помощника формы справиться с этим поведением браузера.

Я считаю, это желательно, чтобы форма HTML, чтобы представить элемент массива пустую строку (так и браузер Rails может каждый ведет себя, как ожидается, в этой ситуации), и использовать ActiveRecord-х reject_if: :all_blank на :evaluations ассоциации (в предположении, что действительно это поведение, которое вы ищете).

Вы можете достичь этого без использования зависаний в виде массива с пустой строкой, удалив из пустых элементов строки из :edema_ids в какой-то момент после его передачи в контроллер, но до того, как он будет оценен :all_blank.

Что-то похожее на:

# Inside MedicalRecordsController 

def create 
    @medical_record = MedicalRecord.create(medical_record_params) 
end 

def update 
    @medical_record = MedicalRecord.find(params[:id]) 
    @medical_record.update_attributes(medical_record_params) 
end 

private 

    def medical_record_params 
    # I assume you're using strong params to control what can be passed 
    # through the controller. If so, manipulate the params *after*  
    # calling .require() and .permit() on the params hash 
    remove_empty_string_from_edema_ids(params) 
    end 

    def remove_empty_string_from_edema_ids(params_hash) # Use a better name than this 
    params_hash[:evaluations].each do |evaluation| 
     # Don't forget to use guard clause to prevent calling a 
     # method on nil if :edema_ids is not present in evaluation 
     evaluation[:edema_ids].reject!(&:empty) if evaluation[:edema_ids] 
    end 
    end 

это (или что-то подобное, так как есть много способов для достижения того же результата) будет - в дополнение к правильно вызывая :all_blank вернуть true если :evaluation действительно все пусто - привести к передаче по-настоящему пустого массива методу оценки edema_ids=(); что благодаря некоторой магии ассоциации ActiveRecord приведет к удалению всего :edemas из этого экземпляра Evaluation.

+0

Это широкие штрихи. Однако, глядя на код контроллера, я неправильно использую 'medical_record_params'. Будет немного обновляться ... – jasonmklug

0

Вы, вероятно, хотите написать собственный reject_if метод. Если вы предоставляете символ, ActiveRecord ищет метод в текущем классе с этим именем и передает хэш-атрибуты атрибутов для evaluation.

class MedicalRecord < ActiveRecord::Base 
    accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :essentially_blank 

    def essentially_blank(attributes) 
    attributes[:description].blank? && attributes[:edema_ids][0].blank? 
    end 
end 

Обратите внимание, что в то время как [""].blank? ложно "".blank? верно, поэтому я получаю первый объект массива и проверки его blank -ness.

Вы можете посмотреть the Rails API, чтобы узнать больше о том, как выполнить пользовательское поведение на reject_if.

+0

Я думал, что существует способ отправить пустой массив вместо массива с пустой строкой. Ок, чтобы написать пользовательский reject_if, он должен работать. Я избегал этого, потому что есть 5+ HABM для оценки :(Спасибо Lanny –

+0

Я бы склонялся к фиксации существования пустой строки (мог передать 'include_hidden: false' в ваш' collection_select') вместо того, чтобы писать пользовательские метод для проверки пустоты Но если вы перейдете по маршруту пользовательского метода, убедитесь, что он достаточно гибкий, чтобы приспособить возможное добавление новых атрибутов в «Оценка». Если в будущем вам нужно добавить атрибут ': title' на «Оценка», и отправил оценку с помощью правильной строки ': title', но пустой': description' и массив ': edema_ids' с пустой строкой, этот метод' essence_blank() 'все равно будет оцениваться как' true' – jasonmklug

+0

@ jasonmklug, я не знал о 'include_hidden: false'. Просто прочитайте об этом, и это тоже имеет смысл. Как бы вы избежали обратного сценария (пользователь явно хочет удалить всех детей из ассоциации)? –