2019年1月23日 星期三

[Vue.js] 父子元件的雙向溝通,簡單的props和emit使用範例


第一部分、利用props將資料傳給components使用 (父元件→子元件)
1. 子元件的設定
首先有一個子元件(child component),我們設定一個propsuserName,型態為String,如果data內的參數,你可以直接在模板裡用{{ userName }}印出,或在function內以this.userName來進行操作。
template的內容是以顯示userName在一個HTML的輸入框裡
// Child Component
Vue.component("child-input", {
  template: `
    <div>
      <label>Name</label>
      <input v-model="childUserName" type="text"/>
    </div>
  `,
  props: {
    // camelCase in JavaScript
    userName: String
  },
  data: function() {
    return {
      childUserName: this.userName
    };
  }
});
※Vue會警告你盡量不要直接修改props參數的值,因此我們設定了childUserName來避免這個問題。
警告內容:
"Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value."

2. 父元件的設定
在HTML裡要用user-name來傳值,也就是說component的命名法遵循camelCase,到HTML內時則是用kebab-case來識別。
user-name和:user-name兩者的用法不同,如果是user-name="initial_input",會把"initial_input"當成字串傳過去,如果前面加上冒號「:」變成:user-name="inital_input",則是把inital_input這個參數的值傳過去。
// HTML
<div id="app">
  <!-- kebab-case in HTML -->
  <child-input :user-name="initial_input"></child-input>
</div>
// JS
new Vue({
  el: "#app",
  data: function() {
    return {
      initial_input: "Annie",
    };
  }
});




第二部分、利用emit將components的資料回傳 (子元件→父元件)
1. 子元件的設定
延續第一部分,我們在methods內新增一個sendToParent的function,@input="sendToParent"代表我們觸發的時機。
看一下sendToParent內容,$emit後面第一個參數"update-text",代表設定一個update-text的事件,第二參數是同時把childUserName這個參數傳出去,如果需要傳更多的參數,直接用逗號分隔接在後面。
Vue.component("child-input", {
  template: `
    <div class="form-group mt-3">
      <label>Name</label>
      <input v-model="childUserName" @input="sendToParent" type="text"/>
    </div>
  `,
  props: {
    userName: String
  },
  data: function() {
    return {
      childUserName: this.userName
    };
  },
  methods: {
    sendToParent: function() {
      this.$emit("update-text", this.childUserName);
    }
  }
});

2. 父元件的設定
父元件的部分也新增一個getChildText的function來接收子元件的資料,當子元件觸發'update-text'時,就會執行getChildText來接收傳送的值,value就是從子元件傳來的childUserName。如果傳多個值,記得在這邊填相應數量的參數來接收。
// JS
new Vue({
  el: "#app",
  data: function() {
    return {
      initial_input: "Annie",
    };
  },
  methods: {
    getChildText: function(value) {
      this.initial_input = value;
    }
  }
});
// HTML
<div id="app">
  <child-input :user-name="initial_input" 
               @update-text="getChildText"></child-input>
</div>


完整的程式碼請參考codepen上的範例:

See the Pen
Sending Messages between Parent and Child
by chenuin (@chenuin)
on CodePen.



相關文章:

2019年1月10日 星期四

[Django] Ubuntu+Apache2+mod_wsgi 部屬Django專案


在開發階段習慣用內建的runserver來啟動Django專案,但官方不建議在正式環境使用,以下介紹如何用ubuntu上安裝apache2和相關的套件來啟動專案,這樣最大的好處就是伺服器開啟時,可以由apache2自動將專案啟動。

Step1. 建立Django專案
※如果已經有現有專案,可以跳過這個步驟。
a. 建立一個獨立的虛擬環境(相關文章:[Python] Virtualenv基本操作)
$ sudo apt install python3-pip
$ sudo pip3 install virtualenv
$ mkdir web_project
$ cd web_project
$ virtualenv env --no-site-packages
b. 安裝Django、新增專案
$ source env/bin/activate
$ pip3 install django
$ django-admin.py startproject myproject .
c. 請在settings.py加上伺服器本身的IP,在開發階段可以用*代替。STATIC_ROOT這行本來在檔案裏面沒有,要手動自己加上去。
# myproject/settings.py
ALLOWED_HOSTS = ["*"]
...
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
d. 加入static的檔案
$ python3 manage.py collectstatic
成功之後資料夾內會多一個static的目錄。
※關於STATIC_ROOT的說明,可以參考『[Django] 如何設定static files(css, javascript, images)』。


Step2. 安裝Apache、mod_wsgi
python2和python3兩者擇一,請注意Django升級到2.0後就不再支援python2了,建議使用python3喔!
# python3
$ sudo apt update
$ sudo apt install apache2 libapache2-mod-wsgi-py3

# python2
$ sudo apt update
$ sudo apt install apache2 libapache2-mod-wsgi
如果希望重開伺服器時,apache2可以自動啟動,請執行下面指令。
$ sudo systemctl enable apache2
完成安裝後,可以在 http://localhost/ 看到apache預設的首頁。


Step3. 設定apache
請先參考一下目前的目錄架構

新建一個Apache的config檔,加入這一段:
# /etc/apache2/sites-available/django.conf
<VirtualHost *:80>
    DocumentRoot /var/www/web_project

    Alias /static /var/www/web_project/static
    <Directory /var/www/web_project/static>
        Require all granted
    </Directory>

    <Directory /var/www/web_project/myproject>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>

    WSGIDaemonProcess myproject python-path=/var/www/web_project python-home=/var/www/web_project/env
    WSGIProcessGroup myproject
    WSGIScriptAlias / /var/www/web_project/myproject/wsgi.py

</VirtualHost>

預設只會讀取default內的設定,請記得啟用自訂的django.conf,並停用default的設定。
$ sudo a2ensite django.conf
$ sudo a2dissite 000-default.conf
$ sudo service apache2 reload
打開 http://localhost/ 就可以看到網站了。


參考資料:
https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-apache-and-mod_wsgi-on-ubuntu-14-04

2019年1月6日 星期日

[Vue.js] 安裝vue-resource執行POST, GET


vue官方之前推薦使用vue-resource套件來執行HTTP Request,不過vue更新到2.0之後,作者就宣告不再更新vue-resource,目前漸漸被axios取代。所以這篇只是做為紀錄性質,寫一下使用vue-resource的方法。

請先用指令npm安裝vue-resource
$ npm install vue-resource

先在專案中加入這個套件
# src/main.js
import VueResource from 'vue-resource'
Vue.use(VueResource)

請根據需求設定{URL} {BODY} {HEADER}

1. GET
this.$http({
 url: {URL},
 method: 'GET',
 body: {},
 headers: {HEADER}
 }).then(function (response) {
  console.log('success');
 }, function (response) {
  console.log('fail');
});

2. POST
this.$http.post({URL}, {BODY}, {HEADER}).then(
 function (response) {
  console.log('success');
 }, function (response) {
  console.log('fail');
});
this.$http({
 url: {URL},
 method: 'POST',
 body: {BODY},
 headers: {HEADER}
 }).then(function (response) {
  console.log('success');
 }, function (response) {
  console.log('fail');
});

2019年1月4日 星期五

[Vue.js] vue-router設定history mode移除網址的#


Vue預設的router模式是hash mode,所以我們能設定成history mode,來去除URL中的#(hashtag),但是除了http://localhost/ 首頁能夠正常顯示,直接打開其他網址會出現404 page no found的錯誤訊息。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

也就是說其實在vue專案裡面實際存在的只有index.html這個頁面,我們必須透過apache設定,將其他網址導到index.html,就能用所有的URL正常的顯示網頁。


Ubuntu設定方式

方法一、mod_rewrite模組
1. 請先確定已經啟用mod_rewrite 模組
$ sudo a2enmod rewrite

2. 編輯apache的config
# /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
    ...
    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

3. 在專案目錄下新增檔案.htaccess
# /var/www/html/.htaccess
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

目錄架構如下
/var/www/html
├── .htaccess
├── index.html
└── static

4. 重啟apache2
$ sudo systemctl restart apache2


方法二、FallbackResource
與方法一相同,只是.htaccess內容改用fallbackresource,目的一樣是改寫URL導向index.html。
# /var/www/html/.htaccess
FallbackResource /index.html
存檔後再重啟apache2就大功告成囉!


參考資料:
https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations

2018年12月9日 星期日

[css/scss] 自適寬度的圖片(Responsive Image)-img隨div大小改變自行縮放


將同一張圖片不切割就能用不同的尺寸顯示,效果預覽如下:


這是利用照片當作背景,並以圖片的短邊為依據,不經剪裁就能以需要的比例呈現,利用css對背景圖片設定的支援功能,設定像是background-position,如果圖片過大時從哪個位置開始進行剪裁,以下為scss撰寫的原始碼。

首先,先準備一個Parent Class,『%』後面加上名稱,在編譯成css後不會看到這個class。
%responsive-image {
 display: flex;
 width: 100%;
 flex: 100%;
 background-size: cover;
 background-repeat: no-repeat;
 background-position: center center;
}

接著設定一個image-1x1的class,使所有的圖片都能以1比1的比例顯示。
// H:1, W:1
.image-1x1{
 @extend %responsive-image;
 padding-bottom: percentage(1 / 1);
}
以此類推,如果需要顯示一個長300、寬400的圖片,請在括號內填入『(3/4)』,就能達到想要的效果!

在html裡面套用這個class。
<div class="image-1x1" style="background-image: url('https://picsum.photos/400/?image=318');"></div>


我搭配bootstrap的grid system寫了一個完整的範例,可以在codepen上找到[連結],請參考:

See the Pen Responsive Image by chenuin (@chenuin) on CodePen.



[css/scss] 水平時間軸(Horizontal Timeline)範例及原始碼分享


水平時間軸


為了方便客製化,這次使用scss來寫,並把幾個重要的參數拉出來寫,其中比較重要的是$event-num,如果有4個項目的話,會用全部的長度均分,因此請依照自己實際使用去修改這個參數。

style.scss
@import url('https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css');

$line-color: #FA8072;  // 水平線的顏色
$point-color: #FF4500; // 圓點的顏色
$point-size: 16px;     // 圓點的大小(直徑) 
$half-point-size: $point-size/2;
$active-font-color: #FA8072;
$inactive-font-color: rgba(0, 0, 0, 0.5);
$event-num: 4;  // 圓點(項目)數量

#timeline {
  ol {
    position: relative;
    display: block;
    margin-top: 50px;
    margin-bottom: 100px;
    height: 1px;
    padding-inline-start: 0;
  }

  li {
    position: relative;
    display: inline-block;
    float: left;
    width: calc(100% /  #{$event-num});
    height: 1px;
    background: $line-color;
    color: $inactive-font-color;
    .diplome {
      text-align: center;
      margin-top: 20px;
    }
    .point {
      content: "";
      display: block;
      width: $point-size;
      height: $point-size;
      border-radius: 50%;
      border: 1px solid $point-color;
      background: #fff;
      position: absolute;
      top: -#{$half-point-size};
      left: calc(50% - #{$half-point-size});
    }
    .timestamp {
      font-size: 14px;
      text-align: center;
    }
    &.active>.point {
      border: $half-point-size solid $point-color;
    }
    &.active>.diplome,
    &.active>.timestamp {
      color: $active-font-color;
    }
  }
}

如果習慣看css的人,可以利用一些線上的轉換網站(如:sassmeister)轉換成css。

index.html
<div class="container">
  <div id="timeline">
    <ol>
      <li class="active">
        <span class="point"></span>
        <h6 class="diplome">My Birthday</h6>
        <p class="timestamp">2018/06/01</p>
      </li>
      <li class="active">
        <span class="point"></span>
        <h6 class="diplome">Father's Day</h6>
        <p class="timestamp">2018/08/08</p>
      </li>
      <li>
        <span class="point"></span>
        <h6 class="diplome">Helloween</h6>
        <p class="timestamp"></p>
      </li>
      <li>
        <span class="point"></span>
        <h6 class="diplome">Christmas</h6>
        <p class="timestamp"></p>
      </li>
    </ol>
  </div>
</div>


原始碼放在codepen上[連結],歡迎參考:

See the Pen Horizontal Timeline by chenuin (@chenuin) on CodePen.


2018年11月18日 星期日

[Github] 用Django專案示範如何使用Travis CI自動測試


開始之前請先到Travis CI[官網]用Github帳號登入
同步github上的專案,並啟動需要測試的專案(Repository)!

請在根目錄新建檔案.travis.yml
使用語言為python,並用3.5和3.6版本。
language: python
python:
  - "3.5"
  - "3.6"

env則是設定這個腳本中的參數,後面install則是安裝這個專案所需的套件。
env:
  - DJANGO_VERSION=2.1
  - DJANGO_VERSION=2.1.2
# command to install dependencies
install:
  - pip install -q Django==$DJANGO_VERSION
  - pip install -r requirements.txt

針對這個專案我寫了一段測試(連結),script就是執行裡面預先寫好的元件測試。
script: python manage.py test

上傳Github後,可以看到測試的結果,我們分別設定了2個版本的python、2個版本Django,所以會產生4個執行結果,就不需要一一下載各個版本來進行測試囉!

如果測試成功會顯示passing,若失敗會顯示failing,我在readme.md加上的標誌方便知道結果,新增方法請參考下面:


完整檔案如下(連結):
# .travis.yml
language: python
python:
  - "3.5"
  - "3.6"
env:
  - DJANGO_VERSION=2.1
  - DJANGO_VERSION=2.1.2
# command to install dependencies
install:
  - pip install -q Django==$DJANGO_VERSION
  - pip install -r requirements.txt
# command to run tests
script: python manage.py test

完整專案請到Github(連結)下載。

2018年11月16日 星期五

[Symfony] Ubuntu16+PHP7 安裝Synfony3.4


這次作業軟體是Ubuntu16.04,安裝的php是7.0。

首先請下載symfony指令,並移到指定資料夾方便全域使用。
sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
sudo chmod a+x /usr/local/bin/symfony

接著馬上就能新增一個專案,my_project可以替換成想要的專案名稱。
# create a symfony project
symfony new my_project 3.4

我第一次建完專案出現訊息提示"simplexml_import_dom() must be available",請指令安裝php-xml排除這個問題。
如果想確定環境是否符合symfony的執行需求,可用下面指令:
php my_project/bin/symfony_requirements

如果看到這樣的畫面,基本上就沒問題,下面還另外有一些建議,可以選擇性安裝!


將Symfony專案啟動的方式
# run application
cd my_project
php bin/console server:run

打開網頁 http://localhost:8080可以看到預設Symfony的頁面,收工囉~


2018年11月13日 星期二

[Boostrap] 透過scss自訂個人風格的主題(Theme)


Bootstrap將常用的Navbar、Button等元件定義好css
可以讓前端的開發更加快速,是最多人使用的前端開發套件!

但有時boostrap的設定不合意要怎麼辦呢?
而且這麼多人使用bootstrap是不是有失個人風格呢?
這時你可以下載scss的版本來定義屬於自己的bootstrap模板喔!

請新建一個檔案加入bootstrap,第一種方法是把所有的元件都引用:
// Custom.scss
// Option A: Include all of Bootstrap

@import "node_modules/bootstrap/scss/bootstrap";

或者有時你只需要使用bootstrap部分的元件
// Custom.scss
// Option B: Include parts of Bootstrap

// Required
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
@import "node_modules/bootstrap/scss/mixins";

// Optional
@import "node_modules/bootstrap/scss/reboot";
@import "node_modules/bootstrap/scss/type";
@import "node_modules/bootstrap/scss/images";
@import "node_modules/bootstrap/scss/code";
@import "node_modules/bootstrap/scss/grid";
以上二選一即可,另外還提供了bootstrap-grid、bootstrap-reboot兩種常用的部分元件集,像bootstrap-grid就是針對網頁排版的所有bootstrap元件集,在RWD的網站時非常便利。

接著,進入正題!例如btn-primary、text-primary等都是藍色(#007bff),如果要自訂為粉紅色(#e83e8c),請將重新定義的顏色寫在引用bootstrap之前,檔案內容如下:
// Custom.scss
$primary: #e83e8c;

@import "node_modules/bootstrap/scss/bootstrap";
所有的primary就會變成你定義的粉紅色(#e83e8c),基本上所有的參數都能在_variables.scss找到原始的設定,只要加上你需要修改的參數,就可以輕鬆打造屬於你的bootstrap主題囉!

參考資料:
https://getbootstrap.com/docs/4.0/getting-started/theming/

2018年11月2日 星期五

[Javascript] 用Javascirpt截圖的小幫手 html2canvas


請先到html2canvas的官網下載套件
https://html2canvas.hertzen.com/

在想要擷取的畫面加上一個id方便來獲取這個元件,這邊設成capture。
<div id="capture"> ... </div>

這時候就可以擷取儲存成canvas,這邊是直接將這個畫面加到網頁的結尾。
html2canvas(document.querySelector("#capture")).then(canvas => {
    // do something
    document.body.appendChild(canvas)
});
querySelector這邊請記得針對你要擷取的元件設定!

完整的範例如下:
<html>
 <head>
<html>
 <head>
  <title>Screenshots with JavaScript</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" >
 </head>

 <body>
  <div class="container">
   <div class="row">
    <div class="col my-5">
     <!-- The Screenshot Component -->
     <div class="target">
      <div class="card text-center">
       <div class="card-header">
        Featured
       </div>
       <div class="card-body">
        <h5 class="card-title">Special title treatment</h5>
        <p class="card-text">With supporting text below as a natural lead-in to additiona    l content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
       </div>
       <div class="card-footer text-muted">
        2 days ago
       </div>
      </div>
     </div>

     <!-- Display Area -->
     <div class="mt-5 result border border-success">
      <p class="text-center">Display Here!</p>
     </div>

     <!-- Download Link -->
     <a herf="#" class="download-link btn">Download</a>
    </div>
   </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
  <script type="text/javascript" src="./html2canvas.min.js"></script>
  <script>
  $(document).ready(function() {
   html2canvas(document.querySelector(".target")).then(canvas => {
     $(".result").append(canvas);
     $(".download-link").attr("href", canvas.toDataURL());
   });
  });
  </script>
 </body>

</html>