文章

Vue multiselect set autofocus and tinymce set autofocus

要在畫面一進來 focus multiselect 的方式: 參考: https://jsfiddle.net/shentao/mnphdt2g/ 主要就是在 multiselect 的 tag 加上 ref (例如: my_multiselect), 另外在 mounted() 執行: this.my_multiselect.$el.focus(); ------ 要在畫面進來 focus tinymce 的方式: 關鍵字請搜尋 tinymce  init_instance_callback https://stackoverflow.com/questions/19829126/tinymce-4-how-to-put-cursor-to-end-of-the-text 這就是解法 還會判斷是不是有內文,有內文的話 會 cursor 到最後一的字後面。

Vue extends 與 mixins 的不同

圖片
photo crop from https://unpkg.com/vue@2.3.0/dist/vue.js 好久以前,大概半年前我用了 Vue Typehead 這個第三方套件,那時候就按照 readme 直接用 extends 用上,半年後我加了好多新功能,把一樣的 methods, data property 拆出去變成 mixins。 extends: VueTypeahead, ---- mixins: [ hasPager, hasSearch ], 今天在整理 code 的時候,就好奇這兩個差別是什麼,看了一下 source code,好像是一樣的作用,最後都是呼叫 mergeOptions,只是 mixins 可以帶很多個 (因為是 Array),extends 就一個。

Vue 註冊全域的 Component

網站上有很多元件非常容易重複使用,例如 Loading 狀態,Alert 文字框,無資料的狀態,分頁等 Component,既然這些元件會時不時的被使用,那為何不直接註冊成 Global Component ?!,這樣就再也不用在每一頁重新 import 一次那些常用的 component 了。 在 new 一個 Vue 之前處理好這些 import 跟 註冊 component // main.js, 匯入所有需要註冊成全域的 component import Paginate from 'vuejs-paginate' import Loading from './components/Loader' import EmptyStatus from './components/EmptyStatus' // Register as global component Vue. component ('paginate', Paginate) Vue. component ('loading', Loading) Vue. component ('empty-status', EmptyStatus) //... new Vue({ el: '#app', .... }); 接著就可以直接在 component 直接使用~

Vue-Resource finally

在 Vue / Vue-Resource 接 api 時候,前陣子有一件事一直很困擾我,那就是 Promise 沒有一個狀態讓我知道已經完成,不論 request 是否成功或失敗,我只能知道 resolve, reject, cache... ,但我希望有一個像 jQuery 的 always 一樣,能夠給我放一個 callback 在 request 完成時呼叫 (不論結果如何)。 今天看到一篇 文章  ( Finally the Promise.prototype.finally() is available ),說 Promise 的 finally 已經在 TC39 ECMAScript Proposal 裡面,現在急要用的話可以走 polyfill,該文章有幾種 polyfill 的做法,可以參考看看。不過我看 Vue-Resource 的原始碼,已經有幫我處理過 finally 的樣子,因此我也沒有特別做 polyfill。 舉個之前為什麼需要 finally 的例子,因為在發 api 之前需要顯示 loading 轉轉轉,但是不管 api 的 http status 是 200 還是其他,在結束 ajax 之後,都應該關掉 loading 轉轉轉: loading. show (); ProductService. getCategory () . then (( res )=> { // success this . category = res.body; loading. close (); }, ( res )=>{ // error this . category = null; loading. close (); }); 寫了兩次的 loading.close 感覺就很麻煩,因此有了 finally,只需要一次: loading. show (); ProductService. getCategory () . then (( res )=> { // success this. category = res.body; }, ( res )=>{ this. category = res

使用 Vue Resource 的 interceptors 處理不同的 http status code

首先你的 Vue 專案也是使用 Vue Resource 處理 http request, 再往下看~ 在 SPA 因為有大量接 API 的需求,但每次都要在獨立的 component 處理不同的 http status code 狀態,實在有點麻煩,比方說遇到 api 的 status 是 401 的話,就要再寫一個 router push 到使用者登入畫面之類的 code,重複的邏輯散落在各處 page component. 因為 vue resource 有個好用的 api 叫做 interceptors,覺得他跟 Laravel 的 middleware 很相似,就是所有 request 都會經過他,做一些處理,因此可以利用 interceptors 過濾每一次 request 的狀態,假如是 401 就導到 login, 假如是 404 就導過去 not found 頁面。 確認有使用 vue resource: import VueResource from 'vue-resource' 確認有 use 它: Vue. use (VueResource) 在 use 之後: Vue .http.interceptors. push (( request , next ) => { next(( response ) => { if (response. status === 401 ) { console.log(' 權限不足 '); // do logout router. push ({ path: ' /login ' }); } if (response. status === 404 ) { router. replace ({ path: ' /404 ' }); } }); }); 參考:  https://stackoverflow.com/questions/37228087/vue-js-interceptor

Vue Router 實作按需加載,將 JavaScript 拆成好幾隻

圖片
一開始使用 Vue 寫 Single Page Application (SPA),頁面越來越多時,編譯出來的 build.js 越來越肥,覺得這樣 request 時 loading 蠻吃緊的,最高甚至到了 3MB 以上,雖然有 nginx 的 gzip 在前面擋著,不過還是有其他方式可以盡量減少檔案大小,因此就打算針對某些 routes 做成按需加載,也就是有走到某個頁面時,再加載該頁面的 JavaScript。 *我的專案是  Vue2  /  Webpack2  /  Vue-Router 1.  首先要在 routes 的 import 寫法改一下,把需要按需加載的頁面,改成 resolve require 的方式: /* import Home from '../pages/Home' import Product from '../pages/Product' import Search from '../pages/Search' */ // 改成 const Home = resolve => require (['../pages/Home'], resolve) const Product = resolve => require (['../pages/Product'], resolve) const Search = resolve => require (['../pages/Search'], resolve) 如果是 webpack2 可以寫成 const Home = () => System.import('../pages/Home') 2. 接著記得要在 webpack 的設定,加上 publicPath 的設定,這樣做當需要按需加載時,才知道網址要怎麼帶,否則網址會是錯的。output.publicPath 怎麼寫可以 參考 哦! 。 假如 publicPath 沒辦法 hard code 在 webpack 設定怎麼辦?  因為每個人開發機的網址可能都不同,這邊的替代方案叫做 configuring webpack public path a

利用 Vue.prototype 來設定全域變數

在 Vue component 裏,隨時都有需要拿取一些設定的值,例如圖床的 domain,因為要組完整的圖片 url,例如某些功能是否要開啟,例如檔案大小的 MB 限制也要寫成設定,例如 api 的 endpoint,網址也不可能是 hard code, 也是統一透過一個設定而來。 寫成獨自的設定,不依賴後端傳設定,方便脫離後端。 我的做法是寫一隻 config,但是又需要丟進去給 Vue, 又不想直接 assing 給 window 物件 (如果要這麼公開的直接丟給 window,根本也不用寫 config.js,也很容易看到四散在各處的 window.xxx = 某個設定的值) 所以我就用了 Vue.prototype ,集中管理所有跟設定有關的部分,傳給 Vue。 首先在專案的目錄的某處,放置這隻充滿設定的檔案,檔名我也叫 config.js const CONFIG = { /** * [XSRF_TOKEN] * @description: xsrf_token * @docs: https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie */ XSRF_TOKEN : decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, "$1")), /** * [API_URL] * @description: 全站發 api 的 endpoint */ API_URL : 'https://yiyingloveart.blogspot.com/demo', PHOTO_URL : 'https://static.xxxx.xxx/' } export { CONFIG }; 接著在專案的主程式 以我來說是 main.js,import config 那隻檔案,另外 import 後命名為 SiteConfig. (*看個人習慣) import { CONFIG as SiteConfig }

在 Google custom search 加上 callback

很多人都習慣直接使用 Google 的 custom search 服務,在自己的網站上加入搜尋框。 假如現在有個需求是,想要紀錄使用者在 custom search 上搜尋的文字紀錄,方便公司做統計,什麼樣的單字是使用者特別想搜尋的,那就會需要一個 callback function 是使用者透過 custom search 後取得使用者 key 的單字。 其實官方文件有提到如何使用 callback, 請參考 ,只是一開始我覺得官方這個範例不是很直覺。後來還是靠 stackoverflow取得  實作參考 。 我是使用這組解法,後來有成功~ window.__gcse = { callback: myCallback }; 自定義一個 function (暫時叫 myCallback) 給 _gcse 的 callback,然後在 myCallback 記得接住 input.gsc-input 的 enter 以及 input.gsc-search-button 的 click 事件,再去取得 gsc-input 的內容值。

在 Vue 使用 SweetAlert2

圖片
本文假設你已經裝好 Vue 以及 Webpack 等設定。 首先需要安裝 sweetalert2: npm i sweetalert2 --save-dev 在主要的程式進入點,例如: main.js (看個人摟) 做 import: import swal from 'sweetalert2' 接著在下方定義 swalPlugin 另外把 sweetalert2 的設定指回去給 Vue.prototype.$swal 你也可以定義別的名稱,例如 Vue.prototype.$sweetalert2 之類的。 const swalPlugin = {} swalPlugin. install = function( Vue ){ swal.setDefaults({ confirmButtonClass : ' btn btn-primary ', cancelButtonClass : ' btn btn-default ', buttonsStyling : false , showCloseButton : true }); Vue. prototype .$swal = swal; } Vue. use (swalPlugin) setDefaults 這些設定可不是空穴來風,他只是用來設定一些全站使用 sweetalert 預設的參數,(sweetalert  官網 有寫,當然在此範例裡,你也可以都不要有任何設定。) 使用 Vue.use 完成註冊之後,接下來你就可以在整個站用 this.$swal,直接這樣呼叫使用 sweetalert2 了。 例如: this.$swal({ text: '你確定要刪除嗎', type: 'question', showCancelButton: true, confirmButtonText: '確定', cancelButtonText: '取消', }).then(function () { // 確定要做的事 }, function() { // 取消要做的事 });

解決 ellipsis 無法在 table-row 之下正常顯示

CSS 的 ellipsis 是用來解決限制文字長度,過長則可以幫你在字尾加上 “...”,讓你不必費心思考怎麼處理 ... ,也不須麻煩後端做字元數量計算,  參考連結 。 --- BTW: 但有個缺點就是只限制在文字只需要顯示最多一行的情況,多行的話則沒有辦法 (應該說辦法是有,只是還不能用 (以後可以期待 https://drafts.csswg.org/css-ui-4/#overflow-string),而且解法我也認為大多是 workaround,除非等新的 css 屬性出來,所以多行擷取字元的話還是交給強大的後端吧,JS 處理顯得有些麻煩) --- 不過問題是因為我參考了網路上 sticky-footer 的做法 ( 參考 ),因為網頁主要的 container, dispay: 是 table-row, 結果在整個網站想要使用 ellipsis 突然就沒有作用,解法是在設定 display:table 的 <body> 上 加上 table-layout: fixed 的屬性。 雖然 table-layout: fixed 這解法有點讓我不太理解 (啊...我也不曉得當初為什麼我會選擇用 table 來做 sticky-footer,大概又是趕時間的衝動 XD),下次使用 sticky-footer 時也許可以改用其他方式 (當然還是要看瀏覽器支援的程度)。

為 Vue 專案加上 eslint

背景 vue2 + webpack2 所需要的 package npm i babel-eslint eslint eslint-config-vue eslint-loader ,  eslint-plugin-vue --save-dev webpack loader 調整為: { // 轉 ES6 以及 處理 Eslint 檢查 test: /\.js$/, use: [ 'babel-loader', 'eslint-loader' ], include: [      path.join(__dirname, './resources/assets/js/') ] }, include 可以換成 exclude, 如果你的專案結構很簡單的話。 另外在 loaderOptionsPlugin 調整為: vue: { loaders: { js: 'babel-loader! eslint-loader ', scss: 'style-loader!css-loader!sass-loader' } }, eslint: { configFile: './.eslintrc' } 接著在根目錄建立 .eslintrc 檔案,他可能這樣,你可以再複寫你的設定: { "extends": ["vue"], "plugins": ["vue"], "rules": { "vue/jsx-uses-vars": 2, "no-unused-vars": 0   } } 參考: 在Vue+Babel+Webpack环境中使用ESLint https://github.com/vuejs/eslint-plugin-vue

升級 Laravel 5.4 出現 Call to undefined method dispatch()

參考  http://stackoverflow.com/questions/42057797/laravel-5-4-artisan-migrate-fails-with-call-to-undefined-method-dispatch 。 如果升級時,執行 php artisan 相關指令沒事,就不用理會這個。但如果出現類似錯誤,先把 bootstrap/cache 底下的檔案都刪掉,然後執行 php artisan cache:clear php artisan view:clear

處理 Safari 無法播放 video mp4 的一些問題

最近開始覺得 Safari 大概是未來的 IE。 在使用 HTML5  的 video 播放影片時,遇到兩種影片播不出來的情況。 情況一 較少人會遇到,就是尚未更新成最新的 OS 作業系統,(我的情況是作業系統 OS X EI Capitan, safari版本: 9.0.2),很不巧的我是用自己 server 的 https,當你的 SSL 憑證不是可以信任的,safari 無法載入 mp4,解法是什麼,就只好把 https 改成 http 了。 這個問題找好久,而且還要找到一台尚未更新 OS 的 Mac 來測真的很有困難。參考:  http://stackoverflow.com/questions/25709713/safari-will-not-play-mp4-over-https ,  Cannot view Quicktime movies over HTTPS in Safari or UIWebView 另外新版的 OS 沒有這個問題,即便我是 https,像我更新到 sierra 就沒有這個問題。 情況二 是針對 iphone 上用 safari 看 video 會出現播放鍵,而且還不會自動播放。 這個問題,要從 2 個方向著手: 1. 請先處理掉播放鍵的顯示,處理的意思就是隱藏。用 css 把 video 標籤的 video::-webkit-media-controls-start-playback-button 隱藏起來,如: video::-webkit-media-controls-start-playback-button { display: none; } 可以參考:  displaying html5 video in ios without play button overlay 2. 自動播放的問題比較麻煩,但是 IOS 10 有辦法,至於 10 以下的就很難說。 video 標籤的自動播放,要加上 autoplay  屬性,若你要針對 ios 10 的 safari,請你再加上 playsinline 的屬性。如: <video loop muted autoplay playsinline> <source src="01.mp

[工作筆記] 原來 IE 不支援 SVG 的 animation...

參考:  [SVG animation is not working on IE11] http://stackoverflow.com/questions/33812303/svg-animation-is-not-working-on-ie11 因為今天在處理一個 svg 的動畫發現在 IE 跑不出來,原本以為是因為使用 stroke-dasharray 跟 stroke-dashoffset... ,結果得到的解答是只有 Edge 會支援 SVG 的 Transition 跟 animation。簡單來說目前沒有特別方便快速的解法,網路上的解法建議是轉用 svg 的 library 像是 GreenSock 。 連我都還沒使用到 Edge 的人了,直接放棄在 IE 上的效果,結案。不過依舊要寫兩份樣式,先偵測瀏覽器是否為 IE,在 <body> 上 append 一個新的 css class,再用 .ie something, 跟 .not-ie something.. 類似的方式去做替換 SVG 的方案。

更新 macOS Sierra 之後 compass 指令不見

昨天更新 macOS Sierra 之後,執行 compass 出現 Operation not permitted - /usr/bin/compass,後來找到了一個方式,就是先切換成 root,再安裝: sudo su sudo gem install -n /usr/local/bin compass 不過要小心,在你更新完作業系統的之後, 也請你先更新 xcode , 再來處理這些 compass 路徑的問題 。xcode 的安裝試試在 cli 執行 xcode-select --install ,或是打開你的 App Store 找到他,然後更新,再來處理 compass 的安裝。 若這個解法不適合你,或者你是使用 homebrew 在安裝的話,你可以參考:  Compass install fails on OS X 10.11 due to new rootless mode #2018 。祝你也順利 :)