2018年12月31日 星期一

【Express】如何把前端資料送到後端?body-parser+JQuery AJAX(或是表單)


▌非AJAX作法(表單做法)

  • 用form表單把資料post到特定路徑下面
  • 此做法會導致畫面重新reload
  • 表單資料會出現在req.body (name會是資料名稱)
  • Name是會POST到後端的重要參數(id只會在前端)
//html
<form action="/" method="POST">
     <input type="text" placeholder="IG帳號" name="account" id="submit_input" value="">
      <button id="button-describe" type="submit">訂閱</button>
</form>
//express
app.post('/', function (req, res) {
    console.log(req.body.account);
})

▌AJAX作法(JQuery作法)

//html
<input type="text" placeholder="IG帳號" id="subscriptionAccount" value="">
<button id="subscriptionButton" type="submit">訂閱</button>
  • 取得input資料,然後把他ajax到伺服器
  • data:{“content”:subscriptionAccount} 會把一個物件傳到後面
  • success:function(res) 可以得到後端伺服器的res(後端自己寫的)
//JQuery
$('#subscriptionButton').on('click', function () {
        var subscriptionAccount = $('#subscriptionAccount').val();
        if (subscriptionAccount == '') {
            toastr.info("請輸入帳號")
        }
        else{
            $.ajax({
                url: "/newaccount",
                dataType: "json",
                data:{"content":subscriptionAccount},
                method:"post",
                success:function(res){
                    console.log(res)
                    if(res.success){
                        toastr.info("訂閱成功");
                    }else{
                        toastr.info("帳號已存在");
                    }
                }
            });
        }
});
//Express.JS

app.post('/newaccount', function (req, res) {
    var content = req.body.content;
    var connectTofirebase = fireData.ref("AccountData").push();
    if(allAccount.indexOf(content) == -1){
        connectTofirebase.set({
            "account": content,
            "date": Date.now()
        }).then(function(){
            fireData.ref('AccountData').once('value',function(snapshot){
                res.send({
                    "success":true,
                    "result":snapshot.val(),
                    "message":"訂閱成功"
                });
            });
        });
    }

    else {
            res.send({
                "success":false,
                "message":"已存在該帳號"
            });
    }

})

▌後端的Express的API寫法

  • API寫法
 res.send({
                    "success":true,
                    "result":snapshot.val(),
                    "message":"訂閱成功"
                });

▌不管怎樣都要安裝body-parser

  • body-parser可以解析前端POST過來的「資料」
npm install body-parser  //安裝body-parser

▌設置參數

//express.js
var bodyParser = require('body-parser');
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
    extended: false
}));




【Heroku】Heroku部署



Heroku部署真的超簡單的,步驟真的很不複雜

訂閱尼桑上線囉!


 ▌Heroku部署
heroku login
git add .
git commit -am "make it better"
git push heroku master
git push heroku master  //把檔案推上去
heroku open  //打開網站

▌Package.json

  • 要記得告訴Heroku的engines
  • 要記得告訴Heroku有什麼npm的dependencies
  • 要記得告訴Heroku如何start
  "engines":{
    "node":"6.11.1"
  },
  "dependencies": {
    "jquery": "^3.3.1",
    "toastr": "^2.1.4"
  },
  "scripts": {
    "start": "node express.js"
  },


2018年12月23日 星期日

【Express】用ejs設定layout,不用重複引入一堆link&script


▌建立一個layout.ejs

在裡面放 <%-body%>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>訂閱尼桑</title>
    <link rel="stylesheet" href="/css/reset.css">
    <script src="/build/jquery.min.js"></script>
    <script src="/build/toastr.min.js"></script>
    <link rel="stylesheet" href="/css/toastr.min.css">
    <link rel="stylesheet" href="/css/style.css">

</head>

<body>
    <%-body%>
</body>
<script src="https://www.gstatic.com/firebasejs/5.7.1/firebase.js"></script>
<script src="/build/script.js"></script>

</html>

▌引入layout.ejs

<% layout('layout') %>
<div>我要的html內容</div>

【Express】用ejs模板渲染html /獲取後端傳來的參數


▌如何渲染html?

一直用res.send(“….“) 很浪費時間,因此可以用EJS/Pug之類的模板語言

▌在express設定使用ejs

npm install ejs-locals --save     //安裝ejs
  • 建立一個view的資料夾,放入ejs檔案(但他是html)
var express = require('express');
var app = express();
app.use(express.static('public'))
----下面這些設定可以直接貼上----
var engine = require('ejs-locals');
app.engine('ejs', engine);  //載入engine
app.set('views', './views') //設定到views資料夾 
app.set('view engine', 'ejs')    //設定ejs引擎
app.get('/', function (req, res) {
    res.render('index')
});
----上面這些設定可以直接貼上----

▌使用ejs獲取後端傳來的變數

  • 變數會放在res.render(‘index’, {變數}) 裡面
  • 在ejs中使用<%= 變數 %>就可以「獲取後端傳來的變數」
    <% - 變數 %> 會被渲染成html
app.get('/', function (req, res) {
    res.render('index', {
        'title': ['學習心得', '閱讀筆記', '最新文章']
    })
});
//傳入ejs(其實應該要用for寫,但我懶得整理)
            <div class="box3"><img src="/images/learining.gif" alt="">
                <h2>
                    <%= title[0]%>
                </h2>
            </div>
            <div class="box3"><img src="/images/book.gif" alt="">
                <h2>
                    <%=title[1]%>
                </h2>
            </div>
            <div class="box3"><img src="/images/article.png" alt="">
                <h2>
                    <%=title[2]%>
                </h2>
            </div>
        </div>

【Express】設定靜態檔案public目錄(使用ing/build需要)


▌為什麼要設定靜態檔案目錄?

因為img src不知道要去哪裡找照片,必須先設定一個目錄,它才知道要去哪裡找。
  • 設定靜態檔案:use(express.static('放靜態檔案的目錄名稱'))
var express = require('express');
var app = express();
app.use(express.static('public'))  //設定靜態檔案目錄
app.get('/', function (req, res) {
    res.send('<html><head></head><body><img src="/1.gif"></img></body></html>')

});
var port = process.env.PORT || 3000
app.listen(1222);

【Express】利用use()中介守門員來驗證嘗試登陸的使用者 / 處理錯誤路徑 / 處理錯誤程式碼


▌use()在幹嘛?

  • 用來當守門員,比如說要進入\user之前,要先經過use的函式驗證

▌use()可以放在get()前面當驗證

  • 當app被use時,通過next()才會進到下一個階段
var express = require('express');
var app = express();

app.use(function (req, res, next) {
    console.log("有人嘗試登陸了");
    next();    //先執行
})

app.get('/user', function (req, res) {
    res.send("WELCOME")
});

▌不用use寫守門員

  • app.get(‘路由’,守門員,function(req,res){…})
var check = function (req, res, next) {
    console.log("有人嘗試登陸了");
    next();
}
app.get('/user/:name', check, function (req, res) {
    res.send("WELCOME")
});

▌如何設定「404錯誤」?

在最後一行貼上res.status(404).send(“找不到路徑!”),當找不到路徑時就會進入到404
app.use(function (req, res, next) {      //1.先執行這一行
    console.log("有人嘗試登陸了");
    next();       //2.通過next()才會進去下面
})

app.get('/user', function (req, res) {    //3.進入user路由
    res.send("WELCOME")
});

app.use(function (req, res, next) {
    res.status(404).send("找不到路徑!")
})

▌如何設定「程式error」?

在最後一行貼上app.use(function (err, req, res, next) { res.status(500).send(“程式錯誤”)})
app.use(function (req, res, next) {
    console.log("有人嘗試登陸了");
    next();
})

app.get('/user/:name', function (req, res) {
    wow() //錯誤的程式碼
    res.send("WELCOME")

});

app.use(function (req, res, next) {
    res.status(404).send("找不到路徑:(")
})

app.use(function (err, req, res, next) {
    res.status(500).send("程式錯誤")
})

【Express】用params的去讀取路徑&用query去搜索資訊


params在做什麼?

params會傳到後端,用在讀取網址路徑所用

▌簡介

如果設定是app.get(‘/user/:name‘, function (req, res) {…}
當使用者輸入「http://localhost/user/nissen」,那nissen這個資料就會傳回伺服器,儲存在req.params這個物件中
console.log(req.params.name;) // nissen

▌範例

  • req.params會儲存:name中的資料
var express = require('express');
var app = express();

app.get('/user/:name', function (req, res) {
    var myName = req.params.name;  
    console.log(myName)
    res.send("Hello!" + myName);
});

var port = process.env.PORT || 3000; //如果前面是flase就會進入3000
app.listen(1222);

qurery在做什麼?

很常用在當在搜索一個大資料內容的細部資料(例如撈10筆資料、五個關鍵字、價格在100元以上的產品)
當使用者輸入「http://localhost/user/nissen?limit=20 & keyword=shirt & color=blue」,參數(query)就會被送到後端,讓後端使用這些參數

▌簡介

  • query可以用來做搜尋
    比如說使用者搜尋 ? limit=20 & keyword=shirt & color=red
    後端就會得到{ limit: ‘20’, keyword: ‘shirt’, color: ‘red’ }
  • 使用者輸入的資料,會存在伺服器的req.query當中
  • 可以用變數去儲存那些變數(像是:var limit = req.query.limit;)
var express = require('express');
var app = express();

app.get('/user/:name', function (req, res) {
    var myName = req.params.name;
    var limit = req.query.limit;
    var keyword = req.query.keyword;
    console.log(req.query)
    res.send("Hello!<br>" + myName + "搜尋" + limit + "筆資料");
});

var port = process.env.PORT || 3000; //如果前面是flase就會進入3000
app.listen(1222);

▌params和query有什麼差別?

  • params:用在讀取網址路徑所用。
  • query:常用在當在搜索一個大資料內容的細部資料(例如撈10筆資料、五個關鍵字、價格在100元以上的產品)

【Express】Express介紹和環境建置+設定路徑、傳送資料、監聽端口


▌Express是什麼?

一個輕量化框架,可以整合firebase+mongoDB+myaql各種資料庫

▌建置Express

npm init
npm install express --save

▌引入Express

var express = require ('express')  //引入express module
var app = express ()  //開啟使用express各種功能

▌設定、傳送和監聽路徑

  • 設定路徑:app.get('路徑',function(req,res){ res.send(“回傳資料”)}) //可以設定各種路徑的結果。例如:app.get(‘/user’,function….)
  • 傳送資料:res.send(“回傳資料”)
  • 監聽端口:app.listen(PORT)
app.get('/',function(req,res){ res.send("傳回資料") })

var port=process.env.PORT||3000  //如果前面是flase就會進入3000
app.listen(port)

▌開啟/關閉伺服器

node express.js //開啟伺服器
nodemon express //隨時更新伺服器
control+c //下架伺服器

▌常見錯誤

  • 通常是因為端口被佔住
    listen EADDRINUSE :::1212
    

【Firebase】基礎的firebase指令整理(連結資料庫、獲取資料、寫入資料、刪除資料)


▌環境建置

▌在尾端加入firebase代碼

<script src="https://www.gstatic.com/firebasejs/5.7.1/firebase.js"></script>
<script src="./js/usefirebase.js"></script>

▌「連接」資料庫

var config = {
 //省略
};
firebase.initializeApp(config);

▌「獲取」資料庫資料

  • 連接資料庫:var 連接資料庫 = firebase.database().ref(“目錄名稱”);
  • 取得資料庫的資料:目錄名稱 .once(“value”, function (snapshot) { var data = snapshot.val() } 他會是一個{key{….},key{…}}的形式
  • for in:可以取出想要的資料放入陣列
var  connectTofirebase = firebase.database().ref("addIgAccount");
addIgAccount.once("value", function (snapshot) {
    var data = snapshot.val()
    igAccountArray = []
    for (item in data) {
        igAccountArray.push(data[item].account)
    }
    console.log(igAccountArray)

})

▌「寫入」資料庫資料

  • 寫入單筆資料:連接資料庫.push(data).then(function(){.....})
//按下按鈕,並且寫入資料
var  connectTofirebase = firebase.database().ref("addIgAccount");
$('#button-describe').click(function () {
    var igInputAccount = $('#submit_input').val();
    if (igInputAccount == '') {
        toastr.info("請輸入帳號");
    } else if ($.inArray(igInputAccount, igAccountArray) == -1) {

        var data = {
            "account": igInputAccount,
            "date": firebase.database.ServerValue.TIMESTAMP
        };
        connectTofirebase.push(data).then(function () {
            toastr.info("訂閱成功");
        }).catch(function (error) {
            toastr.info("請重新嘗試");
        });

    } else {
        toastr.info("已存在該帳號");
    }
})

▌「移除」資料庫資料

  • 移除單筆資料:連接資料庫.child(key).remove().then(function(){...})
  • 注意:要先取得key才可以移除

var  connectTofirebase = firebase.database().ref("addIgAccount");
$(document).ready(function () {
    $('#button-cancel-describe').click(function () {
        var igInputAccount = $('#submit_input').val();
        if (igInputAccount == '') {
            toastr.info("請輸入帳號");
        } else {
            for (item in allAccountData) {
                if (allAccountData[item].account == igInputAccount) {
                    key = item;
                    connectTofirebase.child(key).remove().then(function () {
                        toastr.info("取消訂閱成功");
                    })
                }

            }

        }
    })
});

【Jquery】用slideToggle效果製作隱藏內容


▌效果


▌簡單原理

引入Jquery並使用slideToggle效果即可

▌html 範例模板

簡介:li(標題)+span(icon)+div(內容)的結構
 <div class="box_area">
  <ul>
  <li class="hiring-title" class="box_area_firstline">1.為什麼會有這麼奇怪的訂 閱系統?<span class="icon"></span></li>
 <div class="hiring-detail"> //內容的html  </div>
 <li class="hiring-title">2.為什麼是用ig推送<span class="icon"></span></li>
 <div class="hiring-detail"> //內容的html</div>
 <li class="hiring-title">3.為什麼是用ig推送<span class="icon"></span></li>
 <div class="hiring-detail"> //內容的html</div>
 </ul>
 </div class="box_area">

▌JS範例模板

  • 記得要用ready不然會取不到
  • 當被點擊時,就把next的hiring-detai div呼叫出來
  • 當被點擊時,就把icon加上一個旋轉的class
  • 原理說明:$(this).next(‘.hiring-detail’) 可以找到下一個符合.hiring-detail的dom
  • 原理說明: $(this).find(‘.icon’).toggleClass(“transform-active”); 可以找到this子層中符合.icon的dom,並把它加上class
//呼叫QA效果
$(document).ready(function () {
    //隱藏要呼叫的div
    $('.hiring-detail').hide();

    //呼叫被隱藏的div
    $('.hiring-title').on('click', function () {
        $(this).next('.hiring-detail').slideToggle(400);
        $(this).find('.icon').toggleClass("transform-active");
        return false;
    })
});

▌CSS範例模板

.box_area {
     width: 85%;
     margin: 0 auto;
     .box_area_firstline {
         border-top: 2px solid black;
     }

     ul li {
         padding: 10px;
         list-style-type: none;
         border-bottom: 2px solid black;
         &:hover {
             color: #9e1134;
         }

         .icon {
             float: right;
             vertical-align: middle;
             &::before {
                 content: "+";
                 font-size: 20px;
             }
         }

         .transform-active {
             transform: rotate(45deg)
         }
     }

     .hiring-detail {
         margin: 0px;
         width: 100%;
         background-color: #eeeeee;
         padding: 10px;
         padding-left: 30px;
     }
 }

【實用框架】toastr——美化你的通知訊息


▌ toastr是什麼?

一個可以通知訊息的JS框架,必須和JQ合用

▌參考資料

▌環境建置

npm install jquery --save
npm install toastr --save

▌引入需要的JS和CSS

這個框架要引入jquery.jstoaster.js+toaster.css(可以塞build資料夾)
<script src="build/jquery.min.js"></script>
<script src="build/toastr.min.js"></script>
<link rel="stylesheet" href="build/toastr.min.css">

▌toastr範例

  • toastr.info("訂閱成功"); //呼叫訊息
  • toastr.options = { positionClass: ‘toast-bottom-left’, }; //調整位置
//html
<button id="button-describe">訂閱</button>
//Js
$('#button-describe').click(function () {
//可以放一些和資料庫連接的東西
 toastr.info("訂閱成功");
})
toastr.options = {
    positionClass: 'toast-bottom-left',
};

2018年12月13日 星期四

【JavaScript】Callback和Promise差別在哪?如何實作?


▌Callback和Promise差別在哪

  • Javascript有許多非同步的動作,像是clickAJAXSetinterval
  • 但如果我們想要讓他照我們想要的順序怎麼辦?
兩個方法:callback還有promise
早期:callback(函示裡面裝函式) //但缺點是產生callback hell
ES6:Promise解決(回傳resolve或是reject)

▌Callback

目標:我們希望先get api,在use api,可是由於get api是非同步的,所以加入一個callback()讓他強制在get api才執行。
function getapi(callback) { //得到一個函式
    setTimeout(function () {
        console.log("get api");
        callback()   //讓callback只在get api後才要執行
    }, 1000);
}

getapi(function () { //input 一個函式
    console.log("use api") //這行只會在get api後才執行
})

▌Promise

  • ES6之後的語法
  • getapi = new Promise(function(resolve,reject){ 會異步的函式 resolve() }
  • getapi.then(){function(){ api回來才要執行的函式 }}
寫法一
let getapi = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("i m api");
        resolve() //這行不要忘記
    }, 1000);
})
getapi.then(
    function () {
        console.log("use api")
    }
).then(function () {
    console.log("use api again")
})
寫法二
function getapi() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("i m api");
            resolve()
        }, 1000);
    })
}

getapi().then(
    function () {
        console.log("use api")
    }
).then(function () {
    console.log("use api again")
})