OpenJS Architect로 풀 스택 서버리스 영화 추적기를 구축하는 방법 - 2부
점진적 향상 및 클라이언트 측 JS 제공
기본 기능이 완료되었습니다. 이제 JavaScript로 이 양식을 개선하여 체크박스를 클릭할 때 양식을 제출함으로써 감시된 체크박스를 실시간으로 저장하도록 할 차례입니다.
// public/index.js
let forms = document.querySelectorAll("form[action='/watched']")
for (let f of forms) {
f.querySelector('button').style.display = 'none'
let check = f.querySelector('input[type="checkbox"]')
check.addEventListener('change', function (e) {
f.submit()
}, false)
}
이것은 우리가 만들 수 있는 가장 간단한 점진적 향상 버전입니다. 클라이언트 측 JavavScript는 양식을 선택하고 '저장' 버튼을 숨기고 체크박스에 이벤트 리스너를 추가합니다. 확인란이 변경될 때마다 사용자를 위해 양식을 제출합니다.
별점 및 댓글 추가
우리는 계속해서 응용 프로그램을 더 복잡하게 만들 것입니다. 다음 몇 섹션에서는 "별점 평가"기능과 댓글 시스템을 추가할 것입니다.
먼저 get-index
경로에서 마크업을 변경해 보겠습니다.
// src/http/get-index/index.js
const arc = require('@architect/functions')
const data = require('@begin/data')
exports.handler = arc.http.async(http)
function authControl(account) {
if (account && account.name) {
return `
Welcome back ${account.name}
<form action=/logout method="post">
<button>Logout</button>
</form>`
} else {
let clientID = process.env.GITHUB_CLIENT_ID
let redirectURL = process.env.GITHUB_REDIRECT
let href = `https://github.com/login/oauth/authorize?client_id=${clientID}&redirect_url=${redirectURL}`
return `
<a href='${href}'>Login with GitHub to see a list of movies</a>
`
}
}
function movie({ key, watched, title, rating, review }) {
return `
<form action="/watched" method="post">
<input type="hidden" name="movieId" value="${key}">
<input type="checkbox" data-movieid="${key}" name=watched ${watched ? 'checked' : ''}>
${title}
<input type="text" data-movieid="${key}" name="review" placeholder="leave a review here" value="${review || ''}">
<input type="radio" name="rating" data-movieid="${key}" value="1" ${rating === '1' ? 'checked' : ''}>
<input type="radio" name="rating" data-movieid="${key}" value="2" ${rating === '2' ? 'checked' : ''}>
<input type="radio" name="rating" data-movieid="${key}" value="3" ${rating === '3' ? 'checked' : ''}>
<button class=cage>Save</button>
</form>`
}
async function getMovies(account) {
let movies = [
{ key: '001', title: 'Raising Arizona' },
{ key: '002', title: 'Con Air' },
{ key: '003', title: 'National Treasure' },
]
if (account) {
let accountMovies = await data.get({
table: `${account.id}-movies`
})
let result = ''
for (let mov of movies) {
let found = (accountMovies.find(m => m.key === mov.key))
result += movie({
key: mov.key,
title: mov.title,
watched: !!found,
rating: found ? found.rating : '',
review: found ? found.review : ''
})
}
return result
}
return ''
}
async function http(req) {
return {
html: `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/_static/index.css">
<link rel="shortcut icon" href="#">
<title>Praise Cage</title>
</head>
<body>
<h1>Praise Cage</h1>
${authControl(req.session.account)}
${await getMovies(req.session.account)}
<script src=/_static/index.js type=module></script>
</body>
</html>
`
}
}
다음으로 post-watched
끝점을 수정할 수 있습니다. 이제 데이터베이스에 저장할 review
및 rating
속성을 포함하려고 합니다.
// src/http/post-watched/index.js
const arc = require('@architect/functions')
const data = require('@begin/data')
exports.handler = arc.http.async(route)
async function route(req) {
console.log('post-watched req.body:', req.body )
let account = req.session.account.id
if (req.body.watched) {
await data.set({
table: `${account}-movies`,
key: req.body.movieId,
review: req.body.review,
rating: req.body.rating
})
} else {
await data.destroy({
table: `${account}-movies`,
key: req.body.movieId
})
}
return {
location: '/'
}
}
마지막으로 클라이언트 측 JS를 다음과 같이 업데이트할 수 있습니다.
// public/index.js
let forms = document.querySelectorAll("form[action='/watched']")
console.log(forms)
for (let f of forms) {
// hide all submit buttons
f.querySelector('button').style.display = 'none'
// get a ref to the form checkbox
let check = f.querySelector('input[type="checkbox"]')
// get a ref to the form radios
let radios = f.querySelectorAll('input[type="radio"]')
// get a ref to the form text
let text = f.querySelector('input[type="text"]')
// mutates state
function changed (e) {
let movieId = e.target.dataset.movieid
let payload = { movieId }
payload.watched = f.querySelector('input[name="watched"]').checked
payload.review = f.querySelectorAll('input[name="review"]')[0].value
let rating = f.querySelectorAll('input[name="rating"]:checked')
payload.rating = rating.length === 1 ? rating[0].value : ''
//make an HTTP post with fetch
fetch('/watched', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-nick-cage': 'fetch'
},
body: JSON.stringify(payload)
}).catch(function fail(err) {
console.log('failed', err)
})
}
// listen to checkbox changes
check.addEventListener('change', changed, false)
// listen to radio buttons getting hit
for (let r of radios) {
r.addEventListener('input', changed, false)
}
// listen to changes to review text
text.addEventListener('input', changed, false)
}
전체 소스 코드는 여기에서 찾을 수 있습니다: https://github.com/pchinjr/movie-tracker
Reference
이 문제에 관하여(OpenJS Architect로 풀 스택 서버리스 영화 추적기를 구축하는 방법 - 2부), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/pchinjr/how-to-build-a-full-stack-serverless-movie-tracker-with-openjs-architect-part-2-5heg
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
// public/index.js
let forms = document.querySelectorAll("form[action='/watched']")
for (let f of forms) {
f.querySelector('button').style.display = 'none'
let check = f.querySelector('input[type="checkbox"]')
check.addEventListener('change', function (e) {
f.submit()
}, false)
}
우리는 계속해서 응용 프로그램을 더 복잡하게 만들 것입니다. 다음 몇 섹션에서는 "별점 평가"기능과 댓글 시스템을 추가할 것입니다.
먼저
get-index
경로에서 마크업을 변경해 보겠습니다.// src/http/get-index/index.js
const arc = require('@architect/functions')
const data = require('@begin/data')
exports.handler = arc.http.async(http)
function authControl(account) {
if (account && account.name) {
return `
Welcome back ${account.name}
<form action=/logout method="post">
<button>Logout</button>
</form>`
} else {
let clientID = process.env.GITHUB_CLIENT_ID
let redirectURL = process.env.GITHUB_REDIRECT
let href = `https://github.com/login/oauth/authorize?client_id=${clientID}&redirect_url=${redirectURL}`
return `
<a href='${href}'>Login with GitHub to see a list of movies</a>
`
}
}
function movie({ key, watched, title, rating, review }) {
return `
<form action="/watched" method="post">
<input type="hidden" name="movieId" value="${key}">
<input type="checkbox" data-movieid="${key}" name=watched ${watched ? 'checked' : ''}>
${title}
<input type="text" data-movieid="${key}" name="review" placeholder="leave a review here" value="${review || ''}">
<input type="radio" name="rating" data-movieid="${key}" value="1" ${rating === '1' ? 'checked' : ''}>
<input type="radio" name="rating" data-movieid="${key}" value="2" ${rating === '2' ? 'checked' : ''}>
<input type="radio" name="rating" data-movieid="${key}" value="3" ${rating === '3' ? 'checked' : ''}>
<button class=cage>Save</button>
</form>`
}
async function getMovies(account) {
let movies = [
{ key: '001', title: 'Raising Arizona' },
{ key: '002', title: 'Con Air' },
{ key: '003', title: 'National Treasure' },
]
if (account) {
let accountMovies = await data.get({
table: `${account.id}-movies`
})
let result = ''
for (let mov of movies) {
let found = (accountMovies.find(m => m.key === mov.key))
result += movie({
key: mov.key,
title: mov.title,
watched: !!found,
rating: found ? found.rating : '',
review: found ? found.review : ''
})
}
return result
}
return ''
}
async function http(req) {
return {
html: `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/_static/index.css">
<link rel="shortcut icon" href="#">
<title>Praise Cage</title>
</head>
<body>
<h1>Praise Cage</h1>
${authControl(req.session.account)}
${await getMovies(req.session.account)}
<script src=/_static/index.js type=module></script>
</body>
</html>
`
}
}
다음으로
post-watched
끝점을 수정할 수 있습니다. 이제 데이터베이스에 저장할 review
및 rating
속성을 포함하려고 합니다.// src/http/post-watched/index.js
const arc = require('@architect/functions')
const data = require('@begin/data')
exports.handler = arc.http.async(route)
async function route(req) {
console.log('post-watched req.body:', req.body )
let account = req.session.account.id
if (req.body.watched) {
await data.set({
table: `${account}-movies`,
key: req.body.movieId,
review: req.body.review,
rating: req.body.rating
})
} else {
await data.destroy({
table: `${account}-movies`,
key: req.body.movieId
})
}
return {
location: '/'
}
}
마지막으로 클라이언트 측 JS를 다음과 같이 업데이트할 수 있습니다.
// public/index.js
let forms = document.querySelectorAll("form[action='/watched']")
console.log(forms)
for (let f of forms) {
// hide all submit buttons
f.querySelector('button').style.display = 'none'
// get a ref to the form checkbox
let check = f.querySelector('input[type="checkbox"]')
// get a ref to the form radios
let radios = f.querySelectorAll('input[type="radio"]')
// get a ref to the form text
let text = f.querySelector('input[type="text"]')
// mutates state
function changed (e) {
let movieId = e.target.dataset.movieid
let payload = { movieId }
payload.watched = f.querySelector('input[name="watched"]').checked
payload.review = f.querySelectorAll('input[name="review"]')[0].value
let rating = f.querySelectorAll('input[name="rating"]:checked')
payload.rating = rating.length === 1 ? rating[0].value : ''
//make an HTTP post with fetch
fetch('/watched', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-nick-cage': 'fetch'
},
body: JSON.stringify(payload)
}).catch(function fail(err) {
console.log('failed', err)
})
}
// listen to checkbox changes
check.addEventListener('change', changed, false)
// listen to radio buttons getting hit
for (let r of radios) {
r.addEventListener('input', changed, false)
}
// listen to changes to review text
text.addEventListener('input', changed, false)
}
전체 소스 코드는 여기에서 찾을 수 있습니다: https://github.com/pchinjr/movie-tracker
Reference
이 문제에 관하여(OpenJS Architect로 풀 스택 서버리스 영화 추적기를 구축하는 방법 - 2부), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/pchinjr/how-to-build-a-full-stack-serverless-movie-tracker-with-openjs-architect-part-2-5heg텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)