我已经创建了一个表单向导来创建对象,此后,我想通过创建ObjectCategories表并通过复选框将Categories添加到对象来对对象进行分类。
这适用于Object本身,但是我无法使其适用于向导,向导创建了一个自定义类,然后将该自定义类另存为对象,位于表单的最后一步。我使用了本指南:https://medium.com/@nicolasblanco/developing-a-wizard-or-multi-steps-forms-in-rails-d2f3b7c692ce
我认为我需要通过Goal类访问类别方法,但是我不确定该怎么做。我已经阅读了APIDock上的委托页面,但对我来说意义不大(我将业余项目构建为业余项目,因此没有人问这些愚蠢的问题)
错误:
NoMethodError in Wizards#step2
Showing /home/ubuntu/workspace/GoalMates/app/views/wizards/step2.html.erb where line #40 raised:
undefined method `category_ids' for #<Wizard::Goal::Step2:0x00000007454320>
Extracted source (around line #40):
38
39
40
41
42
43
<%= form.collection_check_boxes :category_ids,
Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
</div>
Goal.rb(实际对象):
class Goal < ApplicationRecord
has_many :goal_tasks, dependent: :destroy
belongs_to :user
has_many :flags, as: :flaggable
has_many :goal_reflections, dependent: :destroy
has_many :goal_categories, dependent: :destroy
has_many :categories, through: :goal_categories
after_update :send_notifications, if: :goalstatus_changed?
profanity_filter! :goalname, :goaldesc, :method => 'hollow'
enum goalstatus: [:active, :paused, :complete, :abandoned]
def set_default_goalstatus
self.goalstatus ||= :active
end
validates :goalname, presence:true, length: {minimum:10, maximum: 240}
validates :goaldesc, presence:true, length: {minimum:10, maximum: 1140}
validates :goalhow, presence:true, length: {minimum:5, maximum: 1140}
validates :goalwhy, presence:true, length: {minimum:5, maximum: 1140}
validates :goalreward, presence:true, length: {minimum:3, maximum: 1140}
validates :goalduedate, presence:true
scope :recentcomplete, -> { where("completed_at > ?", Time.now-7.days) }
scope :activegoal, -> { where("goalstatus = 0") }
scope :pausedgoal, -> { where("goalstatus = 1") }
scope :completedgoal, -> { where("goalstatus = 2") }
scope :abandonedgoal, -> { where("goalstatus = 3") }
scope :recentcomplete, -> { where("completed_at > ?", Time.now-7.days) } #todo this needs changing to complete at..
#also needs changing to last complete
def send_notifications
user.friends.each do |friend|
Notification.create(
originator: user, user: friend, notifiable: self, action: 'update_status'
)
end
end
end
Goal.rb(对象的向导版本,保存在form_models / wizard / goal.rb中)
module Wizard
module Goal
STEPS = %w(step1 step2).freeze
class Base
include ActiveModel::Model
attr_accessor :goal
delegate *::Goal.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :goal
def initialize(goal_attributes)
@goal = ::Goal.new(goal_attributes)
end
end
def set_default_goalstatus
self.goalstatus ||= :active
end
class Step1 < Base
validates :goalname, presence:true, length: {minimum:10, maximum: 240}
validates :goaldesc, presence:true, length: {minimum:10, maximum: 1140}
validates :goalhow, presence:true, length: {minimum:5, maximum: 1140}
end
class Step2 < Step1
validates :goalwhy, presence:true, length: {minimum:5, maximum: 1140}
validates :goalreward, presence:true, length: {minimum:4, maximum: 1140}
validates :goalduedate, presence:true
end
end
end
表单中出现问题的部分:
<%= form.label "What categories is this goal in?" %><br>
<%= form.collection_check_boxes :category_ids,
Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
GoalCategory.rb
class GoalCategory < ApplicationRecord
belongs_to :goal
belongs_to :category
end
Step2.html.erb:
<div class="col-md-10 col-md-offset-1">
<ol class="breadcrumb center">
<li><%= link_to "Step 1", step1_wizard_path %></li>
<li class="active">Step 2</li>
</ol>
<%= render 'layouts/errors', object: @goal_wizard %>
<%= form_for @goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |form| %>
<%= hidden_field_tag :current_step, 'step2' %>
<div class="col-md-12 goalform">
<%= form.label "Why do you have this goal?" %><br>
<%= form.text_area :goalwhy, :rows => 2, style: 'width:80%;',
placeholder: "I want to get in shape for my wedding...", class:"form-control" %>
<p><i>Remembering why you have your goal will motivate you when it gets tough. It also helps in matching you with others.</i></p>
</div>
<div class="col-md-12 goalform">
<%= form.label "Rewarding yourself" %><br>
<%= form.text_area :goalreward, :rows => 2, style: 'width:80%;',
placeholder: "I'll buy a new pair of gym shoes", class:"form-control" %>
<p><i>Achieving a goal is its own reward. Extra rewards, no matter how small, are also great motivation. Treat yourself, you'll have earned it!</i></p>
</div>
<div class="col-md-12 goalform">
<%= form.label "When is your goal due?" %><br>
<%= form.date_select :goalduedate %>
<p><i>Now you've got a target goal, when will you achieve it by? It's important to be
realistic and set an achievable date.
TIP: We often set difficult goals that are hard to achieve and far away. Instead try setting a goal that's 1-2 months away. You can always create more goals!</i></p>
</div>
<div class="col-md-12 goalform">
<%= form.label "What categories is this goal in?" %><br>
<%= form.collection_check_boxes :category_ids,
Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
</div>
<div class="col-md-12 goalform center">
<%= form.submit class:"btn btn-primary", value: "Create Goal" %>
</div>
<% end %>
</div>
目标控制器(对象):
class GoalsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show, :completedgoals]
before_action :set_goal, only: [:show, :edit, :update, :destroy, :mark_completed, :goal_review, :goal_review_next, :toggle_goal_status ]
def index
@goals = Goal.activegoal.order("created_at DESC").page(params[:page]).per(6)
end
def new
@goal = Goal.new
authorize @goal
end
def edit
authorize @goal
end
def create
@goal = Goal.new(goal_params)
@goal.set_default_goalstatus
@goal.user_id = current_user.id
authorize @goal
respond_to do |format|
if @goal.save
format.html { redirect_to @goal, success: 'Goal was successfully created.' }
format.json { render :show, status: :created, location: @goal }
else
format.html { redirect_to @goal, success: 'Goal could not be created.' }
format.json { render json: @goal.errors, status: :unprocessable_entity }
end
end
end
def update
authorize @goal
update_params = goal_params
if update_params['goalstatus'] && update_params['goalstatus'] == 'complete'
update_params['completed_at'] = Time.zone.now
end
respond_to do |format|
if @goal.update(update_params)
format.html { redirect_to @goal, success: 'Goal was successfully updated.' }
format.json { render :show, status: :ok, location: @goal }
else
format.html { render :edit }
format.json { render json: @goal.errors, status: :unprocessable_entity }
end
end
end
def destroy
authorize @goal
@goal.destroy
respond_to do |format|
format.html { redirect_to goals_url, success: 'Goal was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_goal
@goal = Goal.find(params[:id])
end
def goal_params
params.require(:goal).permit(:goalname, :goaldesc, :goalhow, :goalwhy, :goalreward, :goalduedate, :goalstatus, category_ids: [])
end
end
wizards_controller.rb:
class WizardsController < ApplicationController
before_action :load_goal_wizard, except: %i(validate_step)
def validate_step
current_step = params[:current_step]
@goal_wizard = wizard_goal_for_step(current_step)
@goal_wizard.goal.attributes = goal_wizard_params
session[:goal_attributes] = @goal_wizard.goal.attributes
if @goal_wizard.valid?
next_step = wizard_goal_next_step(current_step)
create and return unless next_step
redirect_to action: next_step
else
render current_step
end
end
def create
@goal_wizard.user_id = current_user.id
@goal_wizard.goalstatus = "active"
if @goal_wizard.goal.save
session[:goal_attributes] = nil
redirect_to goal_path(@goal_wizard.id), success: "Goal created!"
else
redirect_to({ action: Wizard::Goal::STEPS.first }, alert: 'There was a problem when creating the goal.')
end
end
private
def load_goal_wizard
@goal_wizard = wizard_goal_for_step(action_name)
end
def wizard_goal_next_step(step)
Wizard::Goal::STEPS[Wizard::Goal::STEPS.index(step) + 1]
end
def wizard_goal_for_step(step)
raise InvalidStep unless step.in?(Wizard::Goal::STEPS)
"Wizard::Goal::#{step.camelize}".constantize.new(session[:goal_attributes])
end
def goal_wizard_params
params.require(:goal_wizard).permit(:goalname, :goaldesc, :goalhow, :goalwhy, :goalreward, :goalduedate, :goalstatus, category_ids: [])
end
class InvalidStep < StandardError; end
end