iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 18
0
Modern Web

從coding到上線-打造自己的blog系統系列 第 18

Day18 前端串接

前端

前端的部份會著重在JS邏輯上面,JS會使用jquery,美化排版等css有使用bootstra不過不會多談,而且我會盡量把bootstrap的部份拔掉。

我的style.css檔案

.hide {
    display: none;
}

body {
    width: 100%;
    background: whitesmoke;
}

.fixed-center {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0 auto;
}

.abs-center-H {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
    margin: 0 auto;
}

.tui-editor-contents {
    font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
    font-size: 1rem;
}

先把登錄窗口建起來,進入view/html/component創建signContainer寫入

{{define "container/sign"}}
   <div id="singContainer" style="border-width:3px;border-style:solid;border-color:#000000;padding:5px;display:block;">
     <article>
       <div>
         <button id="closeSignBtn" type="button">×</button>
         <h4>Sign in</h4>
         <form id="SignForm" action="http://account.dcreater.com/login" method="post">
           <div>
             <label>Your user name</label>
             <input id="InputUserName" name="" class="form-control" type="text">
           </div>
           <div>
             <label>Your password</label>
             <input id="InputPassword" class="form-control" placeholder="******" type="password">
           </div>
           <p id="AlertWrongParam" class="text-danger" style="display:none;">Wrong user name or password!!</p>
           <button id="SigninSubmitBtn" class="btn btn-primary btn-block" type="submit">
             Sign in
           </button>
           No account? <a href="#">Sign up</a>
         </form>
       </div>
     </article>
   </div>
{{end}}

進入js創建sign.js寫入

// this is the id of the form
$("#SignForm").submit(function(e) {

    let url = $(this).attr('action');
    let username = $("#InputUserName").val();
    let password = $("#InputPassword").val();
    let data = new FormData();
    data.append('username', username);
    data.append('password', password);

    $.ajax({
        method: "POST",
        url: url,
        data: data,
        processData: false,
        contentType: false,
        xhrFields: {
            withCredentials: true
        },
        success: function(data){
            SetUserCookie(data.user)
            window.location.reload();
        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log(jqXHR)
            console.log(textStatus)
            console.log(errorThrown)
            $("#AlertWrongParam").show();
        }
    });

    e.preventDefault(); // avoid to execute the actual submit of the form.
});

// click on sign in
$("#SigninBtn").on("click", function(){
    $("#singContainer").show();
});

// click on x
$("#closeSignBtn").on("click", function(){
    HideSignContainer()
});

function HideSignContainer() {
    $("#singContainer").hide();
}

function SetUserCookie(user) {
    let sub = [["text", "subOid"], ["text", "subOuniquename"],["text", "subOnickname"],["text", "subOdescription"]];
    split_strs(user, sub)
    document.cookie = "OwnerList=" + JSON.stringify(user) + ";Path=/;domain=.dcreater.com;Max-Age=2592000;SameSite=Lax";
}

然後是導覽列,創建navigation.html寫入

{{ define "navigation"}}
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
        <div class="container">
            <a href="/">DCreater</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                    aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarResponsive">
                <ul class="navbar-nav ml-auto">
                    <li class="nav-item active hide">
                        <a href="#">Home
                            <span class="sr-only">(current)</span>
                        </a>
                    </li>
                    <li id="UpdateBlogNav" class="nav-item active">
                        <a class="nav-link" href="#UpdateBlog">編輯專案</a>
                    </li>
                    <li id="DeleteBlogNav" class="nav-item active">
                        <button type="button" class="btn btn-dark" id="DeleteBlogBtn">刪除專案</button>
                    </li>
                    <li id="NewBlogNav" class="nav-item active">
                        <a class="nav-link" href="#NewBlog">新專案</a>
                    </li>
                    <li id="UpdateOwnerNav" class="nav-item active">
                        <a class="nav-link" href="/#UpdateOwner">編輯身份</a>
                    </li>
                    <li id="DeleteOwnerNav" class="nav-item active">
                        <button type="button" class="btn btn-dark" id="DeleteOwnerBtn">刪除身份</button>
                    </li>
                    <li id="NewOwnerNav" class="nav-item active">
                        <a class="nav-link" href="/#NewOwner">新身份</a>
                    </li>
                    <li id="ReturnNav" class="nav-item active hide">
                        <a class="nav-link" href="">返回</a>
                    </li>
                    <li >
                        <button id="SigninBtn" type="button">登入</button>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
{{ end }}

在meta/index.html補上

<!-- Navigation -->
{{template "navigation" . }}

<!-- signin window -->
{{template "container/sign"}}

在js創建main.js寫入

blogTypeInvert = {
    1: "project",
    2: "article"
};

let filler = {
    "blog-list-owner": ["text", "onickname"],
    "owner-href": ["href", "ouniquename"],
};

// global
var meta = JSON.parse($("#meta").text());
let path = window.location.pathname.split("/").slice(1);

if (window.location.pathname === "/") {
    rend_root();
} else {
    rend_blog();
    rend_content();
}

解釋:

  • var meta來讓其他js檔案也能存取到這個變數

rend_root

function rend_root() {
    let sub = {
        "blog-list-title": ["text", "bname"],
        "blog-list-description": ["text", "bdescription"],
        "blog-list-createtime": ["text", "bcreatetime"],
        "blog-href": ["href", "burlpath"],
    };

    let result = "";
    for (let i = 0; i < meta.length; i++) {
        result += fill_content("#blog-list-tmpl", meta[i], Object.assign(filler, sub), 1);
    }
    $("div.blog-list").html(result);
}

// get template and fill with data
function fill_content(tmplid, data, filler, len) {
    let tmpl = get_tmpl(tmplid);
    if (tmpl.length === 0) {
        return ""
    }
    let result = "";
    for (let i = 0; i < len; i++) {
        fill_tmpl(tmpl, data, filler, i);
        result += tmpl[0].outerHTML;
    }
    return result
}

function get_tmpl(target) {
    let html = $(target).html();
    return $(html).clone();
}

function fill_tmpl(tmpl, data, filler, index) {
    let value = "";
    for (let [k, v] of Object.entries(filler)) {

        if (Array.isArray(data[v[1]])) {
            value = data[v[1]][index];
        } else {
            value = data[v[1]];
        }

        if (v[0] === "text") {
            fill_text(tmpl.find("." + k), value);
        } else {
            fill_attr(tmpl.find("." + k), v[0], value);
        }
    }
}

function fill_text(node, value) {
    node.text(value);
}

function fill_attr(node, attr, value) {
    if (attr === "href") {
        fill_href(node, value);
    } else {
        node.attr(attr, value);
    }
}

function fill_href(node, value) {
    if (window.location.pathname !== "/" && node.attr("class") === "blog-href") {
        value = window.location.pathname + "/" + value;
    }
    if (value[0] !== "/") {
        value = "/" + value;
    }
    node.attr("href", value);
}

解釋:

  • 讀取template後把資料rend上去

read_content會處理blog內容的渲染,明天會提到

還需要處理owner,在meta/index.html補上

{{template "form/owner"}}

在component創建ownerForm.html寫上

{{ define "form/owner"}}
    <div id="formContainer" class="container my-5">
        <div class="row">
            <div class="col">
                <div class="card mt-3">
                    <div class="card-body">
                        <form id="NewOwnerForm" style="display: block;">
                            <div class="form-group">
                                <label for="NewOwnerName">Owner Name</label>
                                <input id="NewOwnerName" type="text" class="form-control" placeholder="限制50字(不可重複)">
                                <p class="text-danger invisible">超過50字</p>

                                <label for="NewNickName">暱稱</label>
                              <input id="NewNickName" type="text" class="form-control" placeholder="限制50字(必填)">
                                <p class="text-danger invisible">超過50字</p>

                                <label for="NewOwnerDescription">簡述</label>
                                <input id="NewOwnerDescription" type="text" class="form-control" placeholder="簡單自我介紹 (限制255字)">
                                <p class="text-danger invisible">超過255字</p>

                                <button id="OwnerSubmitBtn" type="submit" class="float-right">Submit</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
{{ end }}

在view/js創建owner.js寫入

//global
var ownerList

// rend for login user
let OwnerSelectNav = $("#OwnerSelectNav");
rend_owner();

// rend for owner list
function rend_owner() {
    ownerList = check_login();
    if (ownerList === undefined) {
        show_login();
        return;
    }

    for(let i = 0; i < ownerList.subOid.length; i++){
        OwnerSelectNav.append("<option value=\"" + ownerList.subOid[i] + "\">" + ownerList.subOnickname[i] + "</option>")
    }
    OwnerSelectNav.removeClass("hide");
    OwnerSelectNav.val(ownerList.subOid[0]);
    hide_login();
}

// return owner data if user has login, else return undefined
function check_login(){
    let ownerList = Cookies.get('OwnerList');
    if (ownerList !== undefined) {
        ownerList = JSON.parse(ownerList)
    }
    return ownerList;
}

// show login btn
function show_login(){
    $("#LoginNav").show();
}

// hide login btn
function hide_login(){
    $("#LoginNav").hide();
}

創建account.js寫入

// global
let oid, ownername, nickname, descryption

if(ownerList !== undefined){
    SetNowOwner(ownerList.subOid[0]);
    OwnerSelectNav.removeClass("hide");
}

// check hashtag
HandleNewOwner();

// owner list on change
OwnerSelectNav.on('change', function() {
    SetNowOwner(OwnerSelectNav.val());
    FillForm();
});

function SetNowOwner(in_id) {
    oid = in_id;
    let i = ownerList.subOid.indexOf(oid);
    ownername = ownerList.subOuniquename[i];
    nickname = ownerList.subOnickname[i];
    descryption = i < ownerList.subOdescription.length ? ownerList.subOdescription[i] : "";
}

//  hashtag event
$(window).on('hashchange', function () {
    HandleNewOwner();
});

function HandleNewOwner() {
    // editflag that create editor
    let editFlag = ["#NewOwner", "#UpdateOwner"];

    if (editFlag.includes(window.location.hash)) {
        HandleShowOwnerForm();
    } else {
        HandleHideOwnerForm();
    }

}

$("#DeleteOwnerBtn").on("click", function() {
    let oid = OwnerSelectNav.val();

    let data = new FormData();
    data.append("oid", oid);

    $.ajax({
        url: "/" + ownerList.subOuniquename[ownerList.subOid.indexOf(oid)],
        method: 'DELETE',
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        withCredentials: true,
        success: function(result) {
            $("option[value=\"" + oid + "\"]").remove();
            alert("刪除成功");
        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log(jqXHR);
            console.log(textStatus);
            console.log(errorThrown);
            alert("刪除失敗,請稍後再試");
        },
    });
});

// create a new owner or update exist owner
$("#OwnerSubmitBtn").on("click", function (e) {
    let method = "post";
    let in_name = $("#NewOwnerName").val();

    let data = new FormData();
    data.append("nickname", $("#NewNickName").val());
    data.append("descript", $("#NewOwnerDescription").val());
    data.append("oid", oid);

    if (window.location.hash === "#UpdateOwner") {
        // update
        data.append("newuniname", (in_name === ownername ? "" : in_name));
        method = 'put';
    }

    $.ajax({
        url: "/" + (window.location.hash === "#NewOwner" ? in_name : ownername),
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        method:  method,
        withCredentials: true,
        success: function (data) {
            // redirect
            window.location.href = "https://dcreater.com";
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(jqXHR);
            console.log(textStatus);
            console.log(errorThrown);
            alert("sorry, something error. Please try again later");
        }
    });

    e.preventDefault();
});

function HandleShowOwnerForm() {
    HideBlogList();
    ShowReturnBtn();
    HideNewOwnerBtn();
    ShowForm();
    FillForm();
}

function HandleHideOwnerForm() {
    ShowBlogList();
    HideReturnBtn();
    ShowNewOwnerBtn();
    HideForm();
}

function FillForm() {
    if(window.location.hash === "#NewOwner") {
        return;
    }
    $("#NewOwnerName").val(ownername);
    $("#NewNickName").val(nickname);
    $("#NewOwnerDescription").val(descryption);
}

// show Owner or project form
function ShowForm() {
    $("#formContainer").show();
}

// Hide Owner or project form
function HideForm() {
    $("#formContainer").hide();
}

function HideBlogList() {
    $("main.container").hide();
}

function ShowBlogList() {
    $("main.container").show();
}

function ShowReturnBtn() {
    $("#ReturnNav").show();
}

function HideReturnBtn() {
    $("#ReturnNav").hide();
}

function ShowNewOwnerBtn() {
    $("#NewOwnerNav").show();
    $("#UpdateOwnerNav").show();
}

function HideNewOwnerBtn() {
    $("#NewOwnerNav").hide();
    $("#UpdateOwnerNav").hide();
}

記得在index.html引入

<!--local script-->
<script src="https://asset.dcreater.com/static/js/owner.js"></script>
<script src="https://asset.dcreater.com/static/js/main.js"></script>
<script src="https://asset.dcreater.com/static/js/sign.js"></script>
<script src="https://asset.dcreater.com/static/js/account.js"></script>

總結

把首頁做出來了,明天會寫讓使用者寫文章的線上編輯器

目前的工作環境

.
├── app
│   ├── apperr
│   │   ├── error.go
│   │   └── handle.go
│   ├── common
│   │   └── cookie.go
│   ├── config
│   │   └── app
│   │       ├── app.yaml
│   │       └── error.yaml
│   ├── database
│   │   ├── auth.go
│   │   ├── connect.go
│   │   ├── error.go
│   │   ├── main.go
│   │   └── scheme.go
│   ├── go.mod
│   ├── go.sum
│   ├── log
│   │   ├── logger.go
│   │   └── logging.go
│   ├── main.go
│   ├── middleware
│   │   ├── auth.go
│   │   ├── error.go
│   │   └── log.go
│   ├── router
│   │   ├── account.go
│   │   ├── asset.go
│   │   ├── host_switch.go
│   │   └── main.go
│   ├── serve
│   │   ├── account.go
│   │   ├── asset.go
│   │   ├── auth.go
│   │   ├── main.go
│   │   └── main_test.go
│   ├── setting
│   │   └── setting.go
│   ├── util
│   │   ├── debug
│   │   │   ├── stack.go
│   │   │   └── stack_test.go
│   │   ├── file
│   │   │   └── file.go
│   │   ├── hash
│   │   │   ├── hash.go
│   │   │   └── hash_test.go
│   │   └── random
│   │       └── random.go
│   └── view
│       ├── css
│       ├── html
│       │   ├── component
│       │   │   ├── blogContainer.html
│       │   │   ├── blogList.html
│       │   │   ├── navigation.html
│       │   │   ├── ownerForm.html
│       │   │   └── signContainer.html
│       │   └── meta
│       │       ├── head.html
│       │       └── index.html
│       └── js
│           ├── account.js
│           ├── main.js
│           ├── owner.js
│           └── sign.js
└── database
    └── maindata

上一篇
Day17 檔案處理
下一篇
Day19 線上編輯器
系列文
從coding到上線-打造自己的blog系統30

尚未有邦友留言

立即登入留言