天亮了 昨晚是平安夜
洛神:2号玩家请继续发言
5号:我其实第一轮第二轮都觉得2号蛮好的,因为有点分不清楚预言家给7号容忍度是好人会想的思考量,所以我觉得2号是好的,2号敢保8号我觉得应该蛮好的,9号当然是比7好饱满很多,但是作为好人我还是有点分不出来,所以我比较怀疑10号,不确定他有没有可能做成一张倒钩是因为前面我是第一个点出6号这张牌,10号,再来就是马上可以刀中神刀刀中神,感觉得3跟8号比较有可能,因为在我视角已经把2号放在偏好了,然後3号没有打2打的那麽严重所以又有一点像好人,可能会想从8 10出,着重听8 10
待续..
今天我们透过hotwire来整合聊天
首先建立 messages 的 controller model 跟 views
并把他跟房间做has many的关联
/2021100416xxxx_create_messages.rb
class CreateMessages < ActiveRecord::Migration[6.1]
def change
create_table :messages do |t|
t.references :room, null: false, foreign_key: true
t.string :user_id
t.string :nickname
t.text :content
t.timestamps
end
end
end
把db整合进我们的资料库
$ bundle exec rake db:migrate
class MessagesController < ApplicationController
before_action :set_room, only: %i[ new create ]
before_action :set_message, only: %i[ update edit destroy show ]
def new
@message = @room.messages.new
end
def create
@message = @room.messages.new(message_params)
@message.user_id = current_user.id
@message.nickname = current_user.name
if @message.save
render turbo_stream: turbo_stream.append(:messages, @message)
else
render 'new', layout: false, status: :unprocessable_entity
end
end
private
def set_room
@room = Room.find(params[:room_id])
end
def set_message
@message = Message.find(params[:id])
end
def message_params
params.require(:message).permit(:content, :room_id, :nickname, :user_id)
end
end
/rooms_controller
def show
assign_seat_to_user(@room)
@messages = @room.messages
.order(:created_at)
@new_message = Message.new(room: @room)
end
加上broadcasts让hotwire内建的ActionCable
也就是要把讯息广播到房间里面
/message.rb
class Message < ApplicationRecord
belongs_to :room
broadcasts_to :room
validates :room, :content, presence: true
def edited?
created_at != updated_at
end
end
room这边也需要加上broadcasts
并与messages是一对多的关联
/room.rb
class Room < ApplicationRecord
resourcify
has_many :seats, dependent: :destroy
has_many :messages, dependent: :destroy
broadcasts
end
在room show page中加入讯息的显示
/show.erb
<div id="messages">
<%= render @room.messages %>
</div>
<%= turbo_frame_tag "new_message", src: new_room_message_path(@room), target: "_top" %>
补上messages的view端,所需要的分别是讯息显示partial与form表单
/views/messages/_message.html.erb
<%= turbo_frame_tag dom_id(message), class: 'pb-1 px-3',
data: { controller: 'message', action: 'mouseout->message#toggleActions mouseover->message#toggleActions', 'message-author-id-value': message.user_id } do %>
<div class='row row-cols-auto gx-2'>
<div class='col fw-bold'>
<%= message.nickname %>
</div>
<div class='col'>
<%= local_time message.created_at, format: :short, class: 'fw-light fs-7' %>
</div>
<% if message.edited? %>
<div class='col'>
<span class='fw-light fs-7'>edited <%= local_time message.updated_at, format: :short %></span>
</div>
<% end %>
</div>
<div class='formatted-content'>
<%= message.content %>
</div>
<% end %>
/views/messages/form.html.erb
<%= form_with model: message.persisted? ? message : [message.room, message], data: { controller: 'form', action: 'turbo:submit-end->form#resetForm' } do |f| %>
<%= f.text_field :content, class: 'form-control', required: true, autocomplete: 'off', autofocus: true, placeholder: "Message ##{message.room.name}" %>
<% if message.persisted? %>
<div class='pt-2'>
<%= link_to 'Cancel', @message, class: 'btn btn-sm btn-outline-secondary' %>
<%= f.submit 'Save changes', class: 'btn btn-sm btn-primary'%>
<div>
<% end %>
<% end %>
/views/messages/new.html.erb
<%= turbo_frame_tag @message do %>
<%= render 'messages/form', message: @message %>
<% end %>
在javascript下controller资料夹新增清空输入框的js
/javascipts/controllers/reset_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
reset() {
this.element.reset()
}
}
/views/messages/edit.html.erb
<%= turbo_frame_tag dom_id(@message) do %>
<div data-controller='scroll-into-view' class='py-2'>
<%= render 'form', message: @message %>
</div>
<% end %>
/views/messages/show.html.erb
<%= render @message %>
这样我们就算是把hotwire的turbo_frame设定好了
再来我们研究一点前几天没碰到的turbo_stimulus
先在vite的 /javascripts/entrypoints/application.js 把档案import进来
/javascripts/entrypoints/application.js
import Rails from '@rails/ujs'
import '@hotwired/turbo-rails'
import '../controllers'
import '../channels'
Rails.start()
在controllers的资料夹下
先处理message_controller.js
这样我们就算是把hotwire的turbo_frame设定好了
再来我们研究一点前几天没碰到的turbo_stimulus
先在vite的 /javascripts/entrypoints 把档案import进来
/javascripts/controllers/message_controller.js
import { Controller } from "@hotwired/stimulus"
import { currentUserId } from '../helpers/auth'
export default class extends Controller {
static targets = ['actions']
static values = {
userId: String,
}
connect() {
if (document.querySelectorAll(`#${this.element.id}`).length > 1) {
this.element.remove()
return
}
this.element.scrollIntoView({ block: 'nearest' })
}
toggleActions() {
if (this.hasActionsTarget && this.authorIdValue === currentUserId()) {
this.actionsTarget.classList.toggle('invisible')
}
}
}
/javascripts/controllers/message_list_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.scrollToBottom()
}
scrollToBottom() {
this.element.scrollTop = this.element.scrollHeight
}
}
/javascripts/controllers/scroll_into_view_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.scrollIntoView({ block: 'nearest' })
}
}
在这边设定讯息不能空白
/javascripts/controllers/form_controller.js
import { Controller } from "@hotwired/stimulus"
const ERROR_BLANK = "Can't be blank"
const validatePresence = (input) => {
if (input.hasAttribute('required')) {
if (input.value.trim() === '') {
input.setCustomValidity(ERROR_BLANK)
return false
} else {
if (input.validity.customError) input.setCustomValidity('')
return true
}
} else {
return true
}
}
export default class extends Controller {
connect() {
this.element.querySelectorAll('input[required]').forEach((input) => {
input.addEventListener('input', this.validateInput)
})
}
disconnect() {
this.element.querySelectorAll('input[required]').forEach((input) => {
input.removeEventListener('input', this.validateInput)
})
}
validateInput() {
validatePresence(this) // && validateSomethingElse()
}
resetForm() {
this.element.reset()
}
}
/javascripts/helpers/auth.js
const DATA_ATTR_NAME = 'data-current-user-id'
export const currentUserId = () => {
return (
document
.querySelector(`[${DATA_ATTR_NAME}]`)
?.getAttribute(DATA_ATTR_NAME) || null
)
}
最後再route设定
/routes.rb
resources :rooms do
resources :messages
end
这麽一来讯息就可以出现在房间内罗
下面这几个是特别找的 hotwire 完整专案 我觉得现在资料蛮散的
推荐刚接触hotwire的朋友可以先从这几个完整专案开始接触唷
reference:
$loom 可以等过 $0.118
天黑请闭眼
LINQ(发音为link 但很多人都说LIN Q) 最大的特质是具备资料查询的能力以及和 VB、C#...
承上篇 CSS 小笔记 渐变(淡入淡出):CSS属性 秒数 速度曲线 范例code如下: .g-10...
启动引擎,把车开回夜晚的车阵中,虽然可能只是处在车流中,默默无名的行驶着,或者快速的疾驶着,又或者处...
Android系统内建SQLite供开发者使用,通常用於存放使用者或系统相关的资料,如果资料除了本地...
再写登入的验证及功能 今天我们要来做登入的判断跟动作, 我们在HomeController.php引...