1. 仕様
-
機能仕様
-
3で割り切れる場合は「Fizz」を出力する。
-
5で割り切れる場合は「Buzz」を出力する。
-
両者で割り切れる場合は「FizzBuzz」を出力する。
-
上記の条件以外の場合は番号を返す。
-
指定された回数だけ繰り返し実行する。
-
タイプごとに出力を切り替えることができる。
-
タイプ1は通常、タイプ2は数字のみ、タイプ3は FizzBuzzの場合のみをプリントする。
-
-
-
画面仕様
-
1から100までを1件ずつ画面に表示できる。
-
1から100まで表示された内容を編集して保存できる。
-
編集して保存した内容を確認して削除できる。
-
2. 設計
2.1. TODOリスト
-
✓ 繰り返し実行できるようにする
-
✓ 「Fizz」を出力できるようにする
-
✓ 「Buzz」を出力できるようにする
-
✓ 「FizzBuzz」を出力できるようにする
-
✓ タイプ 1 の場合
-
✓ 3の倍数のときは数の代わりに「Fizz」をプリントできるようにする。
-
✓ 5 の倍数のときは「Buzz」とプリントできるようにする。
-
✓ 3 と 5両方の倍数の場合には「FizzBuzz」とプリントできるようにする。
-
-
✓ タイプ 2 の場合
-
✓ 3 の倍数のときは数をプリントできるようにする。
-
✓ 5 の倍数のときは数をプリントできるようにする。
-
✓ 3 と 5 両方の倍数の場合には値をプリントできるようにする。
-
-
✓ タイプ 3 の場合
-
✓ 3 の倍数のときは数をプリントできるようにする。
-
✓ 5 の倍数のときは数をプリントできるようにする。
-
✓ 3 と 5両方の倍数の場合には「FizzBuzz」とプリントできるようにする。
-
-
✓ カウンター画面
-
✓ 画面を作る。
-
✓ インクリメントできるようにする。
-
✓ デクリメントできるようにする。
-
-
❏ 一覧編集画面
-
✓ 画面を作る。
-
✓ 編集できるようにする。
-
✓ 保存できるようにする。
-
✓ 保存した内容を表示できるようにする。
-
✓ 保存した内容を削除できるようにする。
-
2.2. ユースケース図
2.3. クラス図
2.3.1. パッケージ構成
2.3.2. Presentaion
View
2.3.3. Application
Service
Repository
2.3.4. Infrastructure
2.3.5. Domain
Model
Type
2.4. シーケンス図
3. 開発
Infrastracture
/* eslint-disable prefer-promise-reject-errors */
/* eslint-disable no-unused-vars */
export default class IndexedDbRepository {
constructor (dbName, storeName) {
this._dbName = dbName
this._storeName = storeName
}
createRequest (dbName, storeName) {
const openReq = indexedDB.open(dbName)
openReq.onupgradeneeded = function (event) {
const db = event.target.result
const os = db.createObjectStore(storeName, { keyPath: 'id' })
}
return openReq
}
put (data) {
return new Promise((resolve, reject) => {
const req = this.createRequest(this._dbName, this._storeName)
req.onsuccess = event => {
const db = event.target.result
const trans = db.transaction(this._storeName, 'readwrite')
const store = trans.objectStore(this._storeName)
try {
const putReq = store.put(data)
putReq.onsuccess = () => {
console.log('put data success')
resolve()
}
req.onerror = event => {
console.log('put data fails')
}
trans.oncomplete = () => {
console.log('transactoin complete')
}
} catch (error) {
console.log(error)
reject(error.message)
}
}
})
}
add (aList) {
return new Promise((resolve, reject) => {
const req = this.createRequest(this._dbName, this._storeName)
req.onsuccess = event => {
const db = event.target.result
const trans = db.transaction(this._storeName, 'readwrite')
trans.oncomplete = () => {
console.log('transaction complete')
resolve()
}
trans.onerror = event => {
console.log(event)
reject()
}
const store = trans.objectStore(this._storeName)
aList.forEach(data => {
try {
const addReq = store.add(data)
addReq.onsuccess = () => {
console.log('add data success')
}
addReq.onerror = event => {
console.log(`add data fails ${event.target.error}`)
}
} catch (error) {
console.log(error)
reject(error.message)
}
})
}
})
}
get (keyValue) {
return new Promise((resolve, reject) => {
const req = this.createRequest(this._dbName, this._storeName)
req.onsuccess = event => {
const db = event.target.result
const trans = db.transaction(this._storeName, 'readwrite')
const store = trans.objectStore(this._storeName)
try {
const getReq = store.get(keyValue)
getReq.onsuccess = event => {
const result = event.target.result
console.log(result)
if (result === undefined) {
reject(`id:${keyValue} not found`)
} else {
resolve(result)
}
}
} catch (error) {
console.log(error)
reject(error.message)
}
}
})
}
openCursor () {
return new Promise((resolve, reject) => {
const req = this.createRequest(this._dbName, this._storeName)
req.onsuccess = event => {
const db = event.target.result
const trans = db.transaction(this._storeName, 'readwrite')
const store = trans.objectStore(this._storeName)
try {
const getReq = store.openCursor()
const result = []
getReq.onsuccess = event => {
const cur = event.target.result
if (!cur) return resolve(result)
result.push(cur.value)
cur.continue()
}
getReq.onerror = event => {
console.log(`selectAll fails ${event.target.error}`)
}
} catch (error) {
console.log(error)
reject(error.message)
}
}
})
}
delete (keyValue) {
return new Promise((resolve, reject) => {
const req = this.createRequest(this._dbName, this._storeName)
req.onsuccess = event => {
const db = event.target.result
const trans = db.transaction(this._storeName, 'readwrite')
const store = trans.objectStore(this._storeName)
try {
const deleteReq = store.delete(keyValue)
deleteReq.onsuccess = event => {
console.log('delete success')
resolve()
}
deleteReq.onerror = event => {
console.log(`delete fails ${event.target.error}`)
}
} catch (error) {
console.log(error)
reject(error.message)
}
}
})
}
deleteDatabase () {
return new Promise((resolve, reject) => {
const deleteReq = indexedDB.deleteDatabase(this._dbName)
deleteReq.onsuccess = () => {
console.log('db destroy success')
resolve()
}
deleteReq.onerror = event => {
console.log(`db delete fail ${event.target.error}`)
}
})
}
}
3.1. Application
3.1.1. Repository
import IndexedDbRepository from '../../infrastructure/IndexedDbRepository'
import FizzBuzzEntity from '../../domain/model/fizz-buzz/FizzBuzzEntity'
export default class FizzBuzzRepository extends IndexedDbRepository {
// eslint-disable-next-line no-useless-constructor
constructor (dbName, storeName) {
super(dbName, storeName)
}
save (data) {
return super.put({ id: data.id, list: data.list })
}
saveBatch (entities) {
const aList = entities.map(entity => {
return { id: entity.id, list: entity.list }
})
return super.add(aList)
}
find (keyValue) {
return super.get(keyValue).then(data => {
return new Promise((resolve, reject) => {
const entity = new FizzBuzzEntity(data.list, data.id)
resolve(entity)
})
})
}
selectAll () {
return super.openCursor().then(aList => {
return new Promise((resolve, reject) => {
const entities = aList.map(
data => new FizzBuzzEntity(data.list, data.id)
)
resolve(entities)
})
})
}
delete (keyValue) {
return super.delete(keyValue)
}
destroy () {
return super.deleteDatabase()
}
}
3.1.2. Service
/* eslint-disable no-useless-constructor */
export default class FizzBuzzCommand {
constructor () {}
execute () {}
}
import FizzBuzzCommand from './FizzBuzzCommand'
export default class FizzBuzzValueCommand extends FizzBuzzCommand {
constructor (type) {
super()
this._type = type
}
execute (number) {
return this._type.generate(number).value
}
}
import FizzBuzzCommand from './FizzBuzzCommand'
import FizzBuzzList from '../../../domain/model/fizz-buzz/FizzBuzzList'
export default class FizzBuzzListCommand extends FizzBuzzCommand {
constructor (type) {
super()
this._type = type
this._list = new FizzBuzzList([])
}
execute (number) {
// 配列は0から始まるので1を足す
[...Array(number + 1).keys()]
.slice(1)
.forEach(i => (this._list = this._list.add(this._type.generate(i))))
return this._list.value
}
}
import FizzBuzzValueCommand from './FizzBuzzValueCommand'
import FizzBuzzListCommand from './FizzBuzzListCommand'
import FizzBuzzEntity from '../../../domain/model/fizz-buzz/FizzBuzzEntity'
import FizzBuzzRepository from '../../repository/FizzBuzzRepository'
import FizzBuzzTypeEnum from '../../../domain/type/fizz-buzz/FizzBuzzTypeEnum'
import FizzBuzzList from '../../../domain/model/fizz-buzz/FizzBuzzList'
export default class FizzBuzzService {
constructor (type) {
this._valueCommand = new FizzBuzzValueCommand(type)
this._listCommand = new FizzBuzzListCommand(type)
this._repository = new FizzBuzzRepository('fizzbuzz.db', 'items')
}
static get Type01 () {
return FizzBuzzTypeEnum.Type01
}
static get Type02 () {
return FizzBuzzTypeEnum.Type02
}
static get Type03 () {
return FizzBuzzTypeEnum.Type03
}
static get MAX_COUNT () {
return FizzBuzzList.MAX_COUNT
}
static valueOf (value) {
return FizzBuzzTypeEnum.valuOf(value)
}
generate (number) {
return this._valueCommand.execute(number)
}
generateList (number) {
return this._listCommand.execute(number)
}
save (list) {
const record = new FizzBuzzEntity(list)
return this._repository.save(record)
}
selectAll () {
return this._repository.selectAll()
}
delete (id) {
return this._repository.delete(id)
}
deleteAll () {
return this._repository.destroy()
}
}
3.2. Domain
3.2.1. Model
export default class FizzBuzzEntity {
constructor (list, id) {
this._list = list
this._id = id || FizzBuzzEntity.generateUuid()
}
static generateUuid () {
let uuid = ''
for (let i = 0; i < 32; i++) {
const random = (Math.random() * 16) | 0
if (i === 8 || i === 12 || i === 16 || i === 20) {
uuid += '-'
}
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(
16
)
}
return uuid
}
get id () {
return this._id
}
get list () {
return this._list
}
}
export default class FizzBuzzList {
constructor (list) {
// eslint-disable-next-line curly
if (list.length > FizzBuzzList.MAX_COUNT)
throw new Error(
`FizzBuzzList can't generate over ${FizzBuzzList.MAX_COUNT} items`
)
this._value = list
}
static get MAX_COUNT () {
return 100
}
get value () {
return this._value
}
toString () {
return this._value
}
add (value) {
const result = this._value
result.push(value)
return new FizzBuzzList(result)
}
}
export default class FizzBuzzValue {
constructor (number, value) {
// eslint-disable-next-line curly
if (number < 0)
throw new Error(
`FizzBuzzValue can't generate by minus nnumber ${number}`
)
this._number = number
this._value = value
}
get value () {
return this._value
}
get number () {
return this._number
}
toString () {
return this._value
}
equals (other) {
return this._number === other._number && this._value === other._value
}
}
3.2.2. Type
export default class FizzBuzzType {
constructor () {
this.FIZZ = 'Fizz'
this.BUZZ = 'Buzz'
}
generate (number) {}
isFizz (number) {
return number % 3 === 0
}
isBuzz (number) {
return number % 5 === 0
}
}
import FizzBuzzType from './FizzBuzzType'
import FizzBuzzValue from '../../model/fizz-buzz/FizzBuzzValue'
export default class FizzBuzzType01 extends FizzBuzzType {
// eslint-disable-next-line no-useless-constructor
constructor () {
super()
}
generate (number) {
const isFizz = this.isFizz(number)
const isBuzz = this.isBuzz(number)
if (isFizz && isBuzz) return new FizzBuzzValue(number, this.FIZZ + this.BUZZ)
if (isFizz) return new FizzBuzzValue(number, this.FIZZ)
if (isBuzz) return new FizzBuzzValue(number, this.BUZZ)
return new FizzBuzzValue(number, number)
}
}
import FizzBuzzType from './FizzBuzzType'
import FizzBuzzValue from '../../model/fizz-buzz/FizzBuzzValue'
export default class FizzBuzzType02 extends FizzBuzzType {
// eslint-disable-next-line no-useless-constructor
constructor () {
super()
}
generate (number) {
return new FizzBuzzValue(number, number)
}
}
import FizzBuzzType from './FizzBuzzType'
import FizzBuzzValue from '../../model/fizz-buzz/FizzBuzzValue'
export default class FizzBuzzType03 extends FizzBuzzType {
// eslint-disable-next-line no-useless-constructor
constructor () {
super()
}
generate (number) {
const isFizz = this.isFizz(number)
const isBuzz = this.isBuzz(number)
if (isFizz && isBuzz) return new FizzBuzzValue(number, this.FIZZ + this.BUZZ)
return new FizzBuzzValue(number, number)
}
}
import FizzBuzzType01 from './FizzBuzzType01'
import FizzBuzzType02 from './FizzBuzzType02'
import FizzBuzzType03 from './FizzBuzzType03'
export default class FizzBuzzTypeEnum {
static get Type01 () {
return new FizzBuzzType01()
}
static get Type02 () {
return new FizzBuzzType02()
}
static get Type03 () {
return new FizzBuzzType03()
}
static valuOf (value) {
switch (value) {
case 'one':
return FizzBuzzTypeEnum.Type01
case 'two':
return FizzBuzzTypeEnum.Type02
case 'three':
return FizzBuzzTypeEnum.Type03
default:
throw Error(`${value} is not defined enum type`)
}
}
}
3.3. Presentation
3.3.1. View
export default class Button {
// eslint-disable-next-line no-useless-constructor
constructor () {}
create (id, tableId, label) {
return `
<button
id="${id}"
type="button"
class="btn btn-primary btn-rounded btn-sm my-0"
data-tableid="${tableId}"
>
${label}
</button>
`
}
}
export default class Message {
// eslint-disable-next-line no-useless-constructor
constructor () {}
static get WARNING () {
return 1
}
static get SUCCESS () {
return 2
}
static get DANGER () {
return 3
}
static get selectorId () {
return 'app__message'
}
static create (message, type) {
switch (type) {
case 1:
return `
<div class="alert alert-warning alert-dismissible fade show" role="alert">
${message}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
`
case 2:
return `
<div class="alert alert-success alert-dismissible fade show" role="alert">
${message}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
`
case 3:
return `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
${message}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
`
default:
return `
<div class="alert alert-primary alert-dismissible fade show" role="alert">
${message}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
`
}
}
clear () {
document.querySelector(`#${Message.selectorId}`).innerHTML = ''
}
render (message, type) {
document.querySelector(`#${Message.selectorId}`).innerHTML = Message.create(
message,
type
)
}
}
export default class Table {
// eslint-disable-next-line no-useless-constructor
constructor () {}
create (aList) {
const header = (() => {
return [...Array(10).keys()].map(i => `<th>${i + 1}</th>`)
})()
const body = (() => {
const result = []
let row = 0
let col = []
aList.forEach((v, k) => {
if ((k + 1) % 10 === 0) {
col.push(`<td>${v}</td>`)
result[row++] = col
col = []
} else {
col.push(`<td>${v}</td>`)
}
})
return result
})()
const row = n => body[n].join(' ')
const result = `
<table>
<thead>
${header[0]}
${header[1]}
${header[2]}
${header[3]}
${header[4]}
${header[5]}
${header[6]}
${header[7]}
${header[8]}
${header[9]}
</thead>
<tbody>
<tr>${row(0)}</tr>
<tr>${row(1)}</tr>
<tr>${row(2)}</tr>
<tr>${row(3)}</tr>
<tr>${row(4)}</tr>
<tr>${row(5)}</tr>
<tr>${row(6)}</tr>
<tr>${row(7)}</tr>
<tr>${row(8)}</tr>
<tr>${row(9)}</tr>
</tbody>
</table>
`
return result
}
}
export default class Counter {
constructor (service) {
this._counter = 0
this._service = service
this._value = this._service.generate(this._counter)
}
incrementEvent () {
this._counter += 1
this._value = this._service.generate(this._counter)
this.render(this._selector)
}
decrementEvent () {
this._counter === 0 ? (this._counter = 0) : (this._counter -= 1)
this._value = this._service.generate(this._counter)
this.render(this._selector)
}
renderComponent () {
const renderMainComponent = () => {
const incrementId = `${this._selector.appCounterId}__increment`
const decrementId = `${this._selector.appCounterId}__decrement`
const dispatchEvent = () => {
document
.querySelector(`#${incrementId}`)
.addEventListener('click', this.incrementEvent.bind(this))
document
.querySelector(`#${decrementId}`)
.addEventListener('click', this.decrementEvent.bind(this))
}
const renderCounter = () => {
const createCounter = (incrementId, decrementId) => {
return `
<div class="col-md-1">
<button class="btn btn-primary" id="${decrementId}">
-
</button>
</div>
<div class="col-md-10">
<h1 class="display-1 text-center">${this._value}</h1>
</div>
<div class="col-md-1">
<button class="btn btn-primary" id="${incrementId}">
+
</button>
</div>
`
}
document.querySelector(
`#${this._selector.appCounterId}`
).innerHTML = createCounter(incrementId, decrementId)
}
// eslint-disable-next-line no-unused-vars
const createMainComponent = (events => {
renderCounter()
events()
})(dispatchEvent)
}
// eslint-disable-next-line no-unused-vars
const render = (() => {
renderMainComponent()
})()
}
render (selector) {
this._selector = selector
this.renderComponent()
}
}
/* eslint-disable no-undef */
import Table from './Table'
import Button from './Button'
import Message from './Message'
import FizzBuzzService from '../../../application/service/fizz-buzz/FizzBuzzService'
export default class TableCreateUpdate {
constructor (service) {
this._table = new Table()
this._button = new Button()
this._message = new Message()
this._service = service
this._list = this._service.generateList(FizzBuzzService.MAX_COUNT)
}
changeEvent (e) {
this._service = new FizzBuzzService(
FizzBuzzService.valueOf(e.target.value)
)
this._list = this._service.generateList(FizzBuzzService.MAX_COUNT)
this._selected = e.target.value
this.render(this._selector)
}
saveTableEvent (e) {
const createListFromTable = table => {
const list = []
for (let i = 1; i <= 10; i++) {
for (let j = 0; j < 10; j++) {
list.push(table.rows[i].cells[j].innerText)
}
}
return list
}
const table = document.querySelector(
`#${e.target.dataset.tableid} > table`
)
const result = createListFromTable(table)
this._service
.save(result)
.then(() => {
this._message.render('保存しました', Message.SUCCESS)
this.render(this._selector)
})
.catch(error => {
this._message.render(error, Message.DANGER)
})
}
renderComponent () {
const renderMainComponent = () => {
const renderSelectId = `${this._selector.appCreateUpdateId}__select`
const renderSelectTypeId = `${renderSelectId}--type`
const renderTableId = `${this._selector.appCreateUpdateId}__table`
const renderButtonTableId = `${renderTableId}__button`
const renderButtonTableSaveId = `${renderButtonTableId}--save`
const dispatchEvent = () => {
document
.querySelector(`#${renderSelectTypeId}`)
.addEventListener('change', this.changeEvent.bind(this))
document
.querySelector(`#${renderButtonTableSaveId}`)
.addEventListener('click', this.saveTableEvent.bind(this))
}
const renderSelect = selected => {
const createSelect = id => {
switch (selected) {
case 'two':
return `
<select name="type" id="${id}">
<option value="one">タイプ1</option>
<option selected value="two">タイプ2</option>
<option value="three">タイプ3</option>
</select>
`
case 'three':
return `
<select name="type" id="${id}">
<option value="one">タイプ1</option>
<option value="two">タイプ2</option>
<option selected value="three">タイプ3</option>
</select>
`
default:
return `
<select name="type" id="${id}">
<option value="one">タイプ1</option>
<option value="two">タイプ2</option>
<option value="three">タイプ3</option>
</select>
`
}
}
document.querySelector(`#${renderSelectId}`).innerHTML = createSelect(
renderSelectTypeId
)
document
.querySelector(`#${renderSelectTypeId}`)
.classList.add('browser-default')
document
.querySelector(`#${renderSelectTypeId}`)
.classList.add('custom-select')
}
const renderTable = aList => {
const createTable = aList => {
return this._table.create(aList)
}
const editTable = () => {
$(`#${renderTableId} > table > tbody > tr > td`).on(
'click',
editToggle(this._service)
)
function save (value) {
document.querySelector(
`#${Message.selectorId}`
).innerHTML = Message.create(`${value}に編集しました。`)
}
function editToggle (service) {
let editFlag = false
return function () {
if (editFlag) return
const $input = $('<input>')
.attr('type', 'text')
.val($(this).text())
$(this).html($input)
$('input', this)
.focus()
.keypress(function (e) {
if (e.which === 13) {
const number = $(this).val()
try {
const value = service.generate(number)
save(value)
$(this)
.after(value)
.unbind()
.remove()
editFlag = false
} catch (e) {
document.querySelector(
`#${Message.selectorId}`
).innerHTML = Message.create(e.message, Message.DANGER)
}
}
})
$('input', this)
.focus()
.blur(function (e) {
$(this)
.after($(this).val())
.unbind()
.remove()
editFlag = false
})
editFlag = true
}
}
}
document.querySelector(`#${renderTableId}`).innerHTML = createTable(
aList
)
document
.querySelector(`#${renderTableId} > table`)
.classList.add('table')
document
.querySelector(`#${renderTableId} > table`)
.classList.add('table-striped')
editTable()
}
const renderButtonSave = () => {
const createButton = (id, renderTableId, label) => {
return this._button.create(id, renderTableId, label)
}
document.querySelector(
`#${renderButtonTableId}`
).innerHTML = createButton(
renderButtonTableSaveId,
renderTableId,
'保存'
)
}
// eslint-disable-next-line no-unused-vars
const createMainComponent = (events => {
document.querySelector(
`#${this._selector.appCreateUpdateId}`
).innerHTML = `
<div id="${renderSelectId}"></div>
<div id="${renderTableId}"></div>
<div id="${renderButtonTableId}"></div>
`
renderSelect(this._selected)
renderTable(this._list)
renderButtonSave()
events()
})(dispatchEvent)
}
// eslint-disable-next-line no-unused-vars
const render = (() => {
renderMainComponent()
})()
}
render (selector) {
this._selector = selector
this.renderComponent()
}
}
import Table from './Table'
import Button from './Button'
import Message from './Message'
export default class TableReadDelete {
constructor (service) {
this._table = new Table()
this._button = new Button()
this._message = new Message()
this._service = service
}
changeReadEvent (e) {
const record = this._record.filter(data => data.id === e.target.value)
this._selected = e.target.value
if (record[0] !== undefined) this.render(this._selector)
}
deleteTableEvent (e) {
const id = document.querySelector(
`#${this._selector.appReadDeleteId}__select__select-read`
).value
this._service.delete(id).then(() => {
this._message.render(`${id}を削除しました。`, Message.SUCCESS)
this.render(this._selector)
})
}
deleteAllEvent (e) {
this._service.deleteAll().then(() => {
this._message.render('全てのレコードを削除しました。', Message.SUCCESS)
this.render(this._selector)
})
}
renderComponent () {
const renderMainComponent = () => {
const renderSelectReadId = `${this._selector.appReadDeleteId}__select`
const renderSelectReadSelectId = `${renderSelectReadId}__select-read`
const renderTableReadId = `${this._selector.appReadDeleteId}__table`
const renderButtonDeleteId = `${this._selector.appReadDeleteId}__button`
const renderButtonDeleteSelectId = `${renderButtonDeleteId}--delete-select`
const renderButtonDeleteAllId = `${renderButtonDeleteId}--delete-all`
const dispatchEvent = () => {
document
.querySelector(`#${renderSelectReadSelectId}`)
.addEventListener('change', this.changeReadEvent.bind(this))
document
.querySelector(`#${renderButtonDeleteSelectId}`)
.addEventListener('click', this.deleteTableEvent.bind(this))
document
.querySelector(`#${renderButtonDeleteAllId}`)
.addEventListener('click', this.deleteAllEvent.bind(this))
}
const renderSelectRead = data => {
const createSelectRead = (id, aList) => {
const options = []
aList.forEach(data => {
if (this._selected === data.id) {
options.push(`<option selected>${data.id}</option>`)
} else {
options.push(`<option>${data.id}</option>`)
}
})
return `
<select name="type" id="${id}">
${options.join(' ')}
</select>
`
}
document.querySelector(
`#${renderSelectReadId}`
).innerHTML = createSelectRead(renderSelectReadSelectId, data)
document
.querySelector(`#${renderSelectReadSelectId}`)
.classList.add('browser-default')
document
.querySelector(`#${renderSelectReadSelectId}`)
.classList.add('custom-select')
}
const renderTableRead = aList => {
const createTable = aList => {
return this._table.create(aList)
}
document.querySelector(`#${renderTableReadId}`).innerHTML = createTable(
aList
)
document
.querySelector(`#${renderTableReadId} > table`)
.classList.add('table')
document
.querySelector(`#${renderTableReadId} > table`)
.classList.add('table-striped')
}
const renderButtonDelete = () => {
const createButton = (id, tableId, label) => {
return this._button.create(id, tableId, label)
}
let button = createButton(
renderButtonDeleteSelectId,
renderTableReadId,
'削除'
)
button += createButton(
renderButtonDeleteAllId,
renderTableReadId,
'全削除'
)
document.querySelector(`#${renderButtonDeleteId}`).innerHTML = button
}
// eslint-disable-next-line no-unused-vars
const createMainComponent = (events => {
document.querySelector(
`#${this._selector.appReadDeleteId}`
).innerHTML = `
<div id="${renderSelectReadId}"></div>
<div id="${renderTableReadId}"></div>
<div id="${renderButtonDeleteId}"></div>
`
this._service.selectAll().then(data => {
renderSelectRead(data)
if (data[0] !== undefined) {
if (this._selected !== undefined) {
renderTableRead(
data.filter(v => v.id === this._selected)[0].list
)
} else {
renderTableRead(data[0].list)
}
}
renderButtonDelete()
this._record = data
events()
})
})(dispatchEvent)
}
// eslint-disable-next-line no-unused-vars
const render = (() => {
renderMainComponent()
})()
}
render (selector) {
this._selector = selector
this.renderComponent()
}
}
import FizzBuzzService from '../../../application/service/fizz-buzz/FizzBuzzService'
import Message from './Message'
import Counter from './Counter'
import TableCreateUpdate from './TableCreateUpdate'
import TableReadDelete from './TableReadDelete'
export default class FizzBuzzView {
constructor (type) {
this._type = type || FizzBuzzService.Type01
this._message = new Message()
this._service = new FizzBuzzService(this._type)
this._counterComponent = new Counter(this._service)
this._tableCreateUpdateComponent = new TableCreateUpdate(this._service)
this._tableReadDeleteComponent = new TableReadDelete(this._service)
}
counterEvent (e) {
this._counterComponent.render(this._selector)
this._message.clear()
}
createUpdateEvent (e) {
this._tableCreateUpdateComponent.render(this._selector)
this._message.clear()
}
readDeleteEvent (e) {
this._tableReadDeleteComponent.render(this._selector)
this._message.clear()
}
renderComponent () {
const selector = {
appId: 'app',
msgId: Message.selectorId,
appCounterId: 'fizz-buzz-app-counter',
appCreateUpdateId: 'fizz-buzz-app-create-update',
appReadDeleteId: 'fizz-buzz-app-read-delete',
counterTabId: 'tab-menu01',
createUpdateTabId: 'tab-menu02',
readDeleteTabId: 'tab-menu03',
counterPanelId: 'panel-menu01',
createUpdatePanelId: 'panel-menu02',
readDeletePanelId: 'panel-menu03'
}
const renderMainComponent = () => {
const dispatchEvent = () => {
document
.querySelector(`#${selector.counterTabId}`)
.addEventListener('click', this.counterEvent.bind(this))
document
.querySelector(`#${selector.createUpdateTabId}`)
.addEventListener('click', this.createUpdateEvent.bind(this))
document
.querySelector(`#${selector.readDeleteTabId}`)
.addEventListener('click', this.readDeleteEvent.bind(this))
}
// eslint-disable-next-line no-unused-vars
const createMainComponent = (events => {
document.querySelector(`#${selector.appId}`).innerHTML = `
<div class="py-3">
<section id="menu">
<div class="container">
<h3 id="function-name" class="mb-3">FizzBuzz</h3>
<div id="${selector.msgId}"></div>
<div class="nav nav-tabs" id="tab-menus" role="tablist">
<a
aria-controls="${selector.counterPanelId}"
aria-selected="true"
class="nav-item nav-link active"
data-toggle="tab"
href="#${selector.counterPanelId}"
id="${selector.counterTabId}"
role="tab"
>Counter</a
>
<a
aria-controls="${selector.createUpdatePanelId}"
aria-selected="false"
class="nav-item nav-link"
data-toggle="tab"
href="#${selector.createUpdatePanelId}"
id="${selector.createUpdateTabId}"
role="tab"
>Table(Create&Update)</a
>
<a
aria-controls="${selector.readDeletePanelId}"
aria-selected="false"
class="nav-item nav-link"
data-toggle="tab"
href="#${selector.readDeletePanelId}"
id="${selector.readDeleteTabId}"
role="tab"
>Table(Read&Delete)</a
>
</div>
<div class="tab-content" id="panel-menus">
<div
aria-labelledby="${selector.counterTabId}"
class="tab-pane fade show active border border-top-0 jumbotron"
id="${selector.counterPanelId}"
role="tabpanel"
>
<div
id="${selector.appCounterId}"
class="row d-flex align-items-center"
></div>
</div>
<div
aria-labelledby="${selector.createUpdateTabId}"
class="tab-pane fade border border-top-0"
id="${selector.createUpdatePanelId}"
role="tabpanel"
>
<dvi class="row p-3">
<div
id="${selector.appCreateUpdateId}"
class="col-md-12 order-md-2"
></div>
</dvi>
</div>
<div
aria-labelledby="${selector.readDeleteTabId}"
class="tab-pane fade border border-top-0"
id="${selector.readDeletePanelId}"
role="tabpanel"
>
<dvi class="row p-3">
<div
id="${selector.appReadDeleteId}"
class="col-md-12 order-md-2"
></div>
</dvi>
</div>
</div>
</div>
</section>
</div>
`
events()
})(dispatchEvent)
}
const renderSubComponent = () => {
this._counterComponent.render(selector)
this._tableCreateUpdateComponent.render(selector)
this._tableReadDeleteComponent.render(selector)
this._selector = selector
}
// eslint-disable-next-line no-unused-vars
const render = (() => {
renderMainComponent()
renderSubComponent()
})()
}
render () {
this.renderComponent()
}
}