# 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`