Rails ROR Tutorial render 变量问题

Kirisames · 2022年06月30日 · 最后由 FinnG 回复于 2022年07月01日 · 282 次阅读

背景:

之前做课设的时候用 ROR Tutorial 学了一段时间的 Rails。现在想自己深入学一下 Rails 这块,就重新捡起来。而且也想试试 Rails 7 与教程中不兼容的地方,并且解决这些问题来提升自己。 (不过这次我自己搞不定了)

环境:

wsl 1

Gemfile:

# source "https://rubygems.org"
source "https://gems.ruby-china.com"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "3.0.2"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.0.2", ">= 7.0.2.2"

# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "sprockets-rails"

# Use sqlite3 as the database for Active Record
gem "sqlite3", "~> 1.4"

# Use the Puma web server [https://github.com/puma/puma]
gem "puma", "~> 5.0"

# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"

# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"

# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"

# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder"

# Use Redis adapter to run Action Cable in production
gem "redis", "~> 4.0"

# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
# gem "kredis"

# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
gem "bcrypt", "~> 3.1.7"

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]

# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false

# Use Sass to process CSS
# gem "sassc-rails"

# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"

gem "bootstrap-sass"

group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
  gem "byebug"
end

group :development do
  # Use console on exceptions pages [https://github.com/rails/web-console]
  gem "web-console"

  # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
  # gem "rack-mini-profiler"

  # Speed up commands on slow machines / big apps [https://github.com/rails/spring]
  # gem "spring"

  gem "guard"
  gem "guard-minitest"
end

group :test do
  # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
  gem "capybara"
  gem "selenium-webdriver"
  gem "webdrivers"
  gem "minitest-reporters"
  gem "rails-controller-testing"
end

gem "erb-formatter", "~> 0.3.0"

问题:

ROR Tutorial 7.3 注册失败 - (需要科学上网),中的 7.3.3 错误消息提示小节。

遇到的困难

错误消息未提示。

尝试解决

经查找,发现是 render 之后的@user为 nil 导致。但是依照代码来说,@user应该留存的是 controller 的值。难道说现在的 Rails 在 render 之后会刷新 erb 中引用 controller 的值吗?

相关代码展示

_errors_messages.html.erb

<% if @user.errors.any? %>
    <div id="error_explanation">
        <div class = "alert alert-danger">
            The form contains <%= pluralize(@user.errors.count, "error") %>
        </div>

        <% @user.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
        <% end %>
    </div>
<% end %>

users_controller.rb


class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def show
    @user = User.find(params[:id])
  end

  def create
    @user = User.new(user_params)
    if @user.save

    else
      render 'new'
      # debugger
    end

  end

  private 
    def user_params
      params.require(:user).permit(:name, :email, :password, :password_confirmation)
    end
end


new.html.erb

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>


<div class="row">
    <div class="col-md-6 col-md-offset-3">
        <%= form_for(@user) do |f| %>

        <%= render 'shared/error_messages' %>

        <%= f.label :name %>
        <%= f.text_field :name %>

        <%= f.label :email %>
        <%= f.email_field :email %>

        <%= f.label :password %>
        <%= f.password_field :password %>

        <%= f.label :password_confirmation, "Confirmation" %>
        <%= f.password_field :password_confirmation %>

        <%= f.submit "Create my account", class: "btn btn-primary" %>
        <% end %>
    </div>
</div>

具体代码项目在这 (Github)

form tag 上加上data-turbo="false"试试

render 之后的@user未 nil 导致

这句没看懂,不过可以试着解释一下:@user.errors.any?中如果有错误必然会显示,现在没有,所以肯定是@user里面没有错误,检查一下 model 文件夹的 user.rb,是否定义了一定的 validate,如果没有,那么@user.save时就始终会成功,那么也走不到 else 那边的逻辑了。

难道说现在的 Rails 在 render 之后会刷新 erb 中引用 controller 的值吗?

Rails 的约定就是 Controller 取 request 的传值,走业务逻辑,调用 render 后立刻开始 render view,这个时候应该只有使用 controller 中的值,不应该修改 controller 中的实例变量(也就是 @开头的变量)。

当然,如果你自己写代码肯定可以在 view 中覆盖刷新 controller 的实例变量,但这个应该认为是一种反模式,是框架一般情况下不推荐的。

3 楼 已删除
piecehealth 回复

有用了,谢谢。 我想问问这是为什么呢? turbo 做了什么吗? 如果我想保持 turbo 启用的状态下,依旧实现这个效果应该怎么做呢?

ericguo 回复

实际情况是:在 controller 文件中render 'new'. 前面使用 debugger 调试,@user.errors是有值的。页面使用 console 调试,在提交错误注册信息之后,下方的控制台@user = nil。所以我感觉是不是 render 的时候这个变量被重置了。

Kirisames 回复

turbo 启用的情况下,create action 要 render 一个 create.turbo_stream.erb, 在 create.turbo_stream.erb 用 turbo 相关标签声明你要替换/新增那些 html。

turbo 关闭(传统模式)是整页重新 render,开启则是服务端返回一些 html 片段,页面只局部更新。

render :new 改成:

else
  render :new, status: :unprocessable_entity
end

参考: https://guides.rubyonrails.org/getting_started.html

按我的理解: turbo 启用了之后要发一个 http 状态码回来, 否则按 turbo stream 处理.

另:

def destroy
  redirect_to root_url, status: :see_other
end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号