Rails-Treasure chest1 (自定义Model网址;多语言包; 时区设置, TimeZone类; 格式日期时间; 表单单选UI; 表单多选UI;Select2 Plugin)

2023-02-17,,,,

自定义Model网址: 随机值网址SecureRandom.base58
多语言包, 包括默认语言设置和user自设置。
时区设置, TimeZone类 ,增加user自选时区功能
格式日期时间: xx.created_at.strftime("%Y-%m-%d %H:%M")
表单单选UI
表单单选UI和Select2 Plugin
表单多选UI和Select2 Plugin



自定义Model网址

方案一:网址上除了数字ID,可以再加上文字
方案二:不要用数据库的递增数字ID,而是用一个随机值产生的 ID✅
方案二(改):除了用乱数ID,也可以让用户自定义 ID(在二的基础上修改)


方案一:

在model层使用to_param(*args)方法,别名 ActionController::Parameters#to_query

返回一个接收者的string形式 ,适合用在一个URL 查询字符串。

   def to_param
   self.id #这是Rails默认形式
end

这是Rails默认的路径方法,会默认添加到event_path(@event)

等同于 event_path(@event.to_param),

因此to_params可以改成"#{self.id}-#{self.name}",  返回一个"string",

返回结果作为参数"id=xxxxxx", url生成Parameters: {"id"=>"xxxxxx"}

to_query方法原理:

> params = ActionController::Parameters.new({

>    name: "David",
>    nationality: "Danish"
> })
=> <ActionController::Parameters {"name"=>"David", "nationality"=>"Danish"} permitted: >
> params.to_query
=> "name=David&nationality=Danish"

⚠️: Event.find(params[:id])中的参数会调用to_i,因此后面的非数字会被自动除去。如果写成"#{self.name}-#{self.id}", 就会报错了。因为"Smokey-1"返回的是0


方案二: 随机数✅✅✅

例子url:   http://localhost:3000/events/FNRLRNgbKZWfVP7P

1. 新增一个字段friendly_id,带index, 是unique。

add_column :events, :friendly_id, :string
add_index :events, :friendly_id, :unique => true

2. 使用to_param方法: self.friendly_id

3. 所有相关Controller action 改用 @event = Event.find_by(friendly_id: params[:id])

4. model.rb中,增加一个before_validation :generate_XXX, on:[:create], 然后设置这个方法为friendly_id ||= SecureRandom.base58

方案二(增加自定义)

<div class="form-group">
  <%= f.label :friendly_id %>
  <%= f.text_field :friendly_id, :required => true, class: "form-control"%>
</div>

⚠️ input-Attribute: required代表必须填写。



配置中文语系

如果我们的网站不需要支援多国语系,你可能会觉得这样做有点辛苦,直接将中文写在样板上就好了。

但这个功能对于团队协作开发网站仍然非常有帮助,因为写程式的时候不一定会先确定文案规格,用 I18n 来处理的话,最后只需要让 PM 统一修改翻译词汇档即可。

gem 'rails-i18n' , gem 'devise-i18n'

使用i18n的Rails指导:https://guides.rubyonrails.org/i18n.html 

方法:

1. config/application.rb

class Application < Rails::Application
  config.i18n.default_locale = "zh-CN"
end

2. 新增 config/locales/zh-CN.yml

"zh-CN":
  event_list: 活动列表
  admin:
    event_list: 活动列表管理

3. 修改en.yml

  "en":
+   event_list: Event List
+   admin:
     event_list: Admin Event List

4. 修改view中对应的位置,统统改为t("event_list")和t("admin.event_list")

t是i18n中的方法。


嵌套variable, 使用%{variable_name}

en:

   hello: "HI,  %{name}"

view中 使用 t("hello",  :name => variable_name )

使用gem 'i18n'后:

config.i18n.default_locale = "zh-CN"
config.i18n.available_locales = ["zh-CN", :en,...] 选择加载的locale包, 不写这句就是全加载。

Devise 也有用到 I18n,可以安装 devise-i18n 这个 gem 有开源社区做好的中文翻译:

https://github.com/tigrish/devise-i18n


对model的字段进行翻译:

zh-CN:
  activerecord:
  attributes:
     event:
        name: "活动名称"
        description: "描述"


用户自行切换多语言:

app/views/layouts/application.html.erb

+   <%= link_to "中文版", :locale => "zh-CN", :is => "dd" %>
+ <%= link_to "English", :locale => "en" %>

注意:locale是自行传入url的参数 http://localhost:3000/events?locale=zh-CN&ls=dd

app/controllers/application_controller.rb

+ before_action :set_locale
+
+ def set_locale
+ if params[:locale] && I18n.available_locales.include?( params[:locale].to_sym )
+ session[:locale] = params[:locale]
+ end
+
+ I18n.locale = session[:locale] || I18n.default_locale
+ end

语言系样板:

如果样板内大多是属于较为静态的内容,Rails 也提供了不同语系可以有不同样板,你只要将样板命名加上语系附档名即可

执行 rails g controller pages

编辑 config/routes.rb

    get "/faq" => "pages#faq"

新增 app/views/pages/faq.zh-CN.html.erb

新增 app/views/pages/faq.en.html.erb

如此在英文版的时候就会使用 faq.en.html.erb 这个样板,中文版时使用 faq.zh-CN.html.erb 这个样板。

最后,编辑 app/views/layouts/application.rb 放上 FAQ 页面的连结:

app/views/layouts/application.rb

   <%= yield %>

   <%= link_to "FAQ", faq_path %> 


表单单选UI(固定选项,无Model)

情况:在表单上加一个单选UI, 选项是固定的几个,因此无需Model来存选项。

首先:新增一个字段来存储选项的状态。使用string.

    rails g migration AddStatusToEvent status:string
    在model层上,定义一个常量数组,用于储存选项类型,并validates :status, inclusion: STATUS

然后:把数据库的status, 转化为user可看的中文。用Helper方法转化中文,或用i18n转化。

再后:在views/../_form.html.erb中增加select下拉菜单。在controller中,设置params白名单。

- 分支:少量选项可改用Radio Button UI: radio_button()

⚠️:可以加bootstrap4的美化。

表单的select的写法:

f.select :status, Event::STATUS.map{ |s| [t(s, :scope => "event.status"), s] }, {}, :class => "form-control"

表单的radio的写法:radion_button(method, tag_value, options = {} )

<div class="btn-group" data-toggle="buttons">
  <% Event::STATUS.each do |status| %>
    <label class="btn btn-default <%= (status == f.object.status)? 'active' : '' %>">
      <%= f.radio_button :status, status %>
      <%= t(status, :scope => "event.status")%>
    </label>
  <% end %>
</div>

=> <input type="radio" value="draft" name="event[status]  id="event_status_draft">

⚠️:

    button样式可以用在其他元素上如<label>, 为了提供checkbox/radio按钮button样式,需要加上data-toggle="buttons" 到一个有.btn-group的元素。目的是给按钮加上$().button('toggle')这个jquery方法,这个方法会给button合适的外观。
    CSS的active, 代表选择的button, 需要developer手动添加到<label>
    f是<FormBuilder:0x0007...>表格对象; f.object是<Event::0x0007...>; f.object.status是当前event的状态。
    如果status和event的status相同,则选中这个button. 当进入edit页面时,默认就是原先选择的状态。

i18n的写法:

t是translate(key, options={})的简写:

Lookup:

key可以是单独的key,也可以是a dot-separated key(string和symbols都可)

如:t('date.formats.short')

scope可以是单独的key, a dot-separated key或者an array of keys or dot-separated keys.

如:

I18n.t 'formats.short', :scope => 'date'
I18n.t 'short', :scope => 'date.formats'
I18n.t 'short', :scope => %w(date formats)

t(@event.status, :scope => "event.status")

在zh-CN.yml中:

event:
  status:
  draft: 草稿
  private: 私密
  public: 公开

插入interpolation:

view中使用 t("hello",  :name => variable_name )

在en.yml中

en:  

   hello: "HI,  %{name}"


表单单选 UI 和 Select2 Plugin

例子:Event需要被分类,但这个分类是可以编辑的,非固定的,因此需要新增一个Category Model。

    rails g model category name; Event增加一个category_id,加上index; 建立双方关联。
    建立Category的 controller: rails g controller admin::categories
    后台编辑页面增加一个目录select。对应请求参数加入白名单(controller#event_params)
    前台show页面,显示分类。或者其他外观布景。
    gem 'select2-rails'是一个jQuery Plugin让select菜单看起来非常漂亮。

注意:

第四步,前台页面显示分类时,如果event没有分类会报告nilClass, 需要添加判断或者使用try()方法。

如:@event.category.try(:name)


表单多选UI

例子:给admin后台的user编组

步骤:

    新建Group用于给users分组
    User和Group是多对多关系。新建一个关联Model Memebership
    控制台输入一些group资料。
    多选UI,需要使用checkbox. helper方法可以使用collection_check_boxes()
    controller#user_params的白名单,:group_ids => []
    前台页面修改。为了避免N+1 query可以使用User.includes(:groups).all
    也可以使用select 2插件

详解:

第四步,在edit页面,新增checkbox,

<%= f.label :group_ids %>
<%= f.collection_check_boxes(:group_ids, Group.all, :id, :name )%>

会生成原生html:(下面是部分代码)

<input type="hidden" name="user[group_ids][]" value="">

<input type="checkbox" value="1" name="user[group_ids][]" id="user_group_ids_1">

@user的group_ids,指它的一组group_id集合,作为参数传入controller处理,以此生成membership记录。

collection_check_boxes(method, collection, value_method, text_method, options = {}, &block)

method是 :group_ids 一个关联方法。

collection是 Group.all

value_method是 要存入的值, 因此这里是:id  (group的id)

text_name是显示的文本内容, 因此这里是 :name  (group的name)

这是生成的参数:

Parameters: { "utf8"=>"✓",

       "authenticity_token"=>"fyyJ9Q...",

 "user"=>{"email"=>"admin@example.org", "group_ids"=>["", "1", "2"]},

       "commit"=>"Update",

       "id"=>"1"}

调用Admin::UsersController#update方法

#查找user.id是1的记录:@user = User.find(params[:Id])

SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]

#然后 @user = @user.update(user_params)

#根据参数中"group_ids"=>["", "1", "2"]查询group.id是1, 2的记录
SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 2)

#membership和group表格连接,然后找user_id = 1的数据,没有找到:
SELECT "groups".* FROM "groups" INNER JOIN "memberships" ON "groups"."id" = "memberships"."group_id" WHERE "memberships"."user_id" = ? [["user_id", 1]]

#因此,根据group_id和user_id, 插入2条memberships
 INSERT INTO "memberships" ("user_id", "group_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["user_id", 1], ["group_id", 1], 。。。

 INSERT INTO "memberships" ("user_id", "group_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["user_id", 1], ["group_id", 2], 。。。

Rails-Treasure chest1 (自定义Model网址;多语言包; 时区设置, TimeZone类; 格式日期时间; 表单单选UI; 表单多选UI;Select2 Plugin)的相关教程结束。

《Rails-Treasure chest1 (自定义Model网址;多语言包; 时区设置, TimeZone类; 格式日期时间; 表单单选UI; 表单多选UI;Select2 Plugin).doc》

下载本文的Word格式文档,以方便收藏与打印。