# Ruby(12/14) 五倍紅寶石(第八屆共筆)(2021/8/20)
## Rails更新
https://rubygems.org/gems/rails/versions/6.1.4.1
新專案:
gem install rails
舊專案:
改Gemfile檔案:
gem 'rails', '~> 6.1', '>= 6.1.4.1'
bundle update
- 期末專案不建議前後端分離
最好是拆解成一個人做幾個功能
這樣前後端都會練的到,而且不會有溝通不良的狀況(兩邊都很菜會很容易溝通不良)
## 框架
看框架時要先看他為什麼要做這個框架
Rails 是 basecamp 的副產品(渣渣)
### stimulus
stimulate.js 框架的用意是只想讓你好好的寫 HTML
- 好處:
方便管理、幫你解決囉唆的小事、不用煩惱按鈕是否存在、action 寫法像 Rails
寫 stimulus 過程中有因為有 target 用,所以就不要再用 queryselector 了
#### data-controller
是 HTML 與 JS 之間的橋樑,就像 HTML 跟 CSS 是透過 class 溝通一樣
用 data-controller 可以選到我要的 JS controller
後面的值是對到檔名,不是內容,他會執行檔案裡面的內容
用data...寫的好處是:如果寫錯也沒關係,他就只是個屬性,不會出錯
#### data-action
做事,做 controller 裡面的 function
每個元素都有預設行為,ex:button 是 click,可以不用寫
#### data-index-target
選取到目標 = queryselector
通常 this 指的是 controller 本身
#### data-controllername-index-value
選到目標的 value
#### connect()
當東西被掛載上去就會執行裡面的動作
類似 initialize,被執行到的時候就會執行裡面的動作
## 打包
以前 Ruby 是用 assets pipeline
現在用 webpacker gem
對webpack來說全部檔案都是js檔
### 改 JS 資料夾名稱
先改資料夾名稱成你想要的名字
去 config/webpacker.yml 檔案
把 source_path: app/javascript 改名
把 javascript 改成你想要的名字
## 上線日期
- 幫 note 新增一個上線欄位
- g migration
- add column
- db:migrate
- index:_form
- 改strong params
- 到 view 那邊顯示出來
## 挑套件
看用的人多不多、有沒有在維護、最近一次更新是啥時、license 是甚麼
### flatpickr
非常輕量級的日曆套件
[flatpickr](https://flatpickr.js.org/)
### wysiwyg editor
what you see is what you get
所見及所得
### medium editor
medium 編輯器套件
[medium-editor](https://www.npmjs.com/package/medium-editor)
## evernote 專案
- html_save
Rails 怕使用者在你表單內輸入程式碼,攻擊你的網站使用者
預設讓輸進去的東西是字串,不會變成程式碼解讀
如果要讓他知道你輸入的東西確定是安全的可以在資料後面加 html_save 讓他安心
show.html.erb
```rb
<%# 用 data-controller 選到 favorite_controller.js %>
<%# data-favorite-id-value 對應到 favorite_controller.js %>
<%# favorite 對應到controller 名字,id 對應到 static values 的屬性名稱 %>
<%# 讓他的 value 對應到 @note.id %>
<%# data-action 執行 favorite controller 裡的 toggleFavorite 方法 %>
<div data-controller="favorite"
data-favorite-id-value="<%= @note.id %>">
<a href="#"
id="favorite_btn"
data-action="favorite#toggleFavorite">
<%= favorite_icon(current_user, @note) %>
</a>
</div>
<%# 當使用者輸入資料有換行時會出現 \n %>
<%# simple_format 將換行從 \n 改成 HTML 標籤的 <br> ,預設是用 <p> 標籤包住單行文字 %>
<%= simple_format(@note.content) %>
```
user.rb
```rb
# 是否有加到最愛,把它變方法看起來比較易懂
# 商業邏輯 = 處理這件事的邏輯、演算法、流程、公式與規則,這邊要處理如何顯示按讚
# 通常會用到條件判斷式
def favorite?(n)
favorite_notes.exists?(n.id)
end
```
notes_helper.rb
```rb
# 當 view 需要用到邏輯時,可以丟到 helper 裡,讓 view 呈現東西就好
module NotesHelper
def favorite_icon(user, note)
# 自己定義一個 classes 然後控制加進來的東西是甚麼來按讚或取消
# 因為看手冊 tag 小幫手會變成陣列,所以classes要設定成陣列
classes = ['favorite_icon']
# 如果目前用戶有登入而且如果有按讚就顯示(把favorite-on加入classes)
# 有登入沒有按讚就不顯示(把favorite-off加入classes)
# favorite?作用在 user 上,所以要定義在 user 的 model
if user
classes << (user.favorite?(note) ? 'favorite-on' : 'favorite-off')
end
# tag 小幫手(TagHelper),可以生出 HTML 的 tag,這邊會生出 div 這個標籤
# tag 或 content_tag 都能達到一樣效果
# 本身引用模組 OutputSafetyHelper,裡面的 safe_join 方法有 html_safe 效果
# 後面新增兩個 class 第一個是上面創造出來的 classes
# 第二個是"data-favorite-target" 在 ruby 檔裡面表示 HTML 的屬性或值
# 要用字串包起來,不然他看不懂
# 在 HTML 會變成 data-favorite-target = "icon"
tag.div class: classes, "data-favorite-target": "icon"
end
end
```
application.js
```js
// 匯入預設的 function
import Rails from "@rails/ujs";
import Turbolinks from "turbolinks";
// 匯入整個模組,指定名稱為 ActiveStorage
import * as ActiveStorage from "@rails/activestorage";
import "channels";
Rails.start();
Turbolinks.start();
ActiveStorage.start();
// import "controllers" 可以省略 .js 跟 index
// = import "controllers/index.js"
// = import "controllers/index"
import "controllers";
// 匯入 styles 資料夾的 index.js
// 而裡面的 index.js 又有匯入 form.scss 跟 ui.scss
// 所以可以抓到所有 css
import "styles";
```
favorite_controller.js
```js
import { Controller } from "stimulus";
// 原本是用 axios 打 api ,現在改用 Rails 內建的 ujs 去打
// 使用前要先引入模組
import Rails from "@rails/ujs";
// 預設輸出讓瀏覽器執行裡面內容
export default class extends Controller {
// static = 靜態方法,static 後面的是類別,可直接被呼叫
// 因為是類別所以可以用 new 創造實體,創造出來的實體不能被呼叫
static targets = ["icon"];
// 轉型成數字
static values = { id: Number };
// 透過 data-action 可以直接選取到按讚的按鈕
// this 指的是 a 標籤,也就是按讚的按鈕本身
// 把自己的 id 傳入 addFavorite 方法
// idValue 是從 data-favorite-id-value 來的
// 在 show.erb 抓到 @note.id,用 idValue 表示
toggleFavorite(e) {
e.preventDefault();
this.addFavorite(this.idValue);
}
// 把剛剛抓到的 id 塞進來
// 負責打 api ,成功就加入或移除按讚
addFavorite(id) {
const url = `/api/v1/notes/${id}/favorite`;
// Rails 內建的 ujs 打 api,會自動幫你抓 token
// url:發送非同步請求的對象,也就是索求資料的 server 的網址
// method:發送請求的類型,如 GET、POST、DELETE、PATCH 等
// data:要附帶在請求裡發送給 server 的資料
// dataType:要求 server 回傳的資料格式
// success:成功後要執行的 function,function 會帶入 server 回傳的資料
Rails.ajax({
url: url,
type: "post",
data: "",
success: (data) => {
// this = favorite_controller 本身,因為箭頭函式沒有自己的 this
// 會去找 class 本身 = favorite_controller.js
// iconTarget 會選到 data-favorite-target 屬性
// this.iconTarget = 選到 "favorite" 裡面的 "icon" class ,
// 也就是用 tag 小幫手生出來的 div
const icon = this.iconTarget;
if (data.status === "added") {
icon.classList.remove("favorite-off");
icon.classList.add("favorite-on");
} else {
icon.classList.remove("favorite-on");
icon.classList.add("favorite-off");
}
},
error: function (err) {
console.log(err);
},
});
}
}
```
datepicker_controller.js
```js
import { Controller } from "stimulus";
import flatpickr from "flatpickr";
// 因為官網的 link 引用的 css 路徑是 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
// 所以猜測要輸入路徑是 "flatpickr/dist/flatpickr.min.css"
import "flatpickr/dist/flatpickr.min.css";
// https://stimulus.hotwired.dev/reference/controllers
// https://flatpickr.js.org/getting-started/#usage
// 看 Usage 有寫到 If using flatpickr in a framework, its recommended to pass the element directly
// flatpickr(element, {});
// 但在stimulus 的 controller 裡面取得 HTML 元素要用 this.element(看官網)
export default class extends Controller {
connect() {
flatpickr(this.element, {});
}
}
```
_form.html.erb
```rb
<%= form_for(note) do |f| %>
<div>
<%= f.label :title, "標題" %>
<%= f.text_field :title %>
<%= f.label :online_date, "上線時間" %>
<%# 在 text_field 寫 data: { controller: 'datepicker' } %>
<%# 到 HTML 後會變成 data-controller="datepicker" %>
<%# 這樣才能被 stimulus.js 拿到,取得 datepicker_controller.js 檔案內容 %>
<%= f.text_field :online_date, data: { controller: 'datepicker' }
%>
</div>
<div>
<%= f.label :content, "內文" %>
<%# 同上,可以取得取得 editor_controller.js 檔案內容 %>
<%= f.text_area :content, data: { controller: 'editor' } %>
</div>
```
editor_controller.js
```js
import { Controller } from "stimulus";
import MediumEditor from "medium-editor";
// 官網的 cdn 路徑 <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/medium-editor@latest/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
// 把版本拿掉就可以正常運作了
import "medium-editor/dist/css/medium-editor.min.css";
// https://www.npmjs.com/package/medium-editor
// 官網的 usage 有寫 You can also pass a list of HTML elements:
// var elements = document.querySelectorAll('.editable'),
// editor = new MediumEditor(elements);
// 因為有用 data: { controller: 'editor' } 所以就不用再用 querySelectorAll 去抓
// 直接寫 new MediumEditor(this.element) 就好
// this 的用法跟 datepicker_controller.js 一樣是 stimulus 來的
export default class extends Controller {
connect() {
new MediumEditor(this.element);
}
}
```
---
###### tags: `Ruby` `Rails`