<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/vuex@2.0.0"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="semantic/semantic.min.js"></script>
<link rel="stylesheet" href="semantic/semantic.min.css">
<div style="width: 350px">
<br>
<!-- 2.1. header -->
<div id="header">
<div class="ui compact menu">
<a class="item">
<i class="icon product hunt"></i> Products
<div class="floating ui red label">{{getCount()}}</div>
</a>
</div>
<br><br>
<button type="button" class="ui green button" v-on:click="load()">Load</button>
<button type="button" class="ui red button" v-on:click="save()">Save</button>
</div>
<br>
<!-- 2.2. products -->
<div id="products" class="ui cards" style="height: 60%; overflow-y: scroll; width: '18rem'">
<product v-for="obj in listFilteredSorted()" v-bind:obj="obj" v-bind:key="obj.id"></product>
</div>
<!-- 2.2.1. product template -->
<template id="template1">
<div class='ui card'>
<div class='content'>
<div class='ui header'>{{obj.name}}</div>
<div class="description">
<button type='button' class='ui blue button' v-on:click="setEditObj(obj)">
Edit
</button>
<a href='#' class='ui grey button' v-on:click="Delete1(obj.id)">Delete</a>
</div>
</div>
</div>
</template><br>
<!-- 2.3. Search & Create Product -->
<h4 class="ui header">
Search & Create Product
</h4>
<div class="ui input focus" id="new">
<input type="text" id="searchAndCreateName"placeholder="Search..." v-on:keyup="keyUp" ref="searchAndCreateName"/>
<button class="ui active violet button" v-on:click="add()" :disabled="getDisabled()"><i class="icon product hunt"></i>Add</button>
</div>
<!-- 2.4. Editing product -->
<div id="edit" class='ui card mini modal' v-on:>
<div class='content'>
<div class='header'>Edit</div>
<div class="ui input focus description">
<input id="inputEdit" type="text" :value="getEditing().name" v-on:keyup="keyUp"/>
</div>
</div>
<div class='extra content'>
<button type="button" class="ui gray button" v-on:click="close">Close</button>
<button type='button' class='ui blue button' v-on:click="editOk">Edit</button>
</div>
</div>
<script src="products.js"></script>
<script>
$('#edit').modal('setting', {
autofocus: false,
onVisible: function () {
setTimeout(function () {
$('#inputEdit').focus();
$('#inputEdit').select();
}, 10);
}
});
</script>
const state = {
title: "",
filter: "",
editing: { id: "", name: ""},
list: []
}
function nextId(state) {
var maxId = 0;
state.list.forEach(function(item,index,arr) {
if (item.id > maxId)
maxId = item.id;
})
return maxId+1;
}
const mutations = {
Delete(state,id) {
state.list.forEach(function(item,index,arr) {
if (item.id == id)
return arr.splice(index,1)
})
},
load(state) {
fetch("/products/load", { method: 'post' })
.then(results => { return results.json() })
.then(json => {
state.list = json.list;
})
},
save(state) {
fetch("/products/save", { method: 'post', headers: {'Content-Type': 'application/json'},body: JSON.stringify(state) })
},
add(state) {
state.list.push({ id: nextId(state), name: state.filter})
state.filter = "";
},
editingKeyUp(state,name) {
state.editing.name = name;
},
filterKeyUp(state,name) {
state.filter = name;
},
setEditObj(state,obj) {
state.editing = Object.assign({}, obj);
},
editOk(state) {
state.list = state.list.map(
(item) => {
if (item.id == state.editing.id)
item.name = state.editing.name;
return item;
}
);
$("#edit").trigger('click.dismiss.bs.modal')
state.editing = { id: "", name: ""};
}
}
var store = new Vuex.Store({
state: state,
mutations: mutations,
getters: {
// 8.1. Useful function to filter and sort product list
listFilteredSorted: state => {
return state.list.filter(
(product) => {
return product.name.indexOf(state.filter) != -1
}
).sort(function (a,b) { return (a.name > b.name) - (a.name < b.name)})
}
}
})
var header = new Vue({
el: '#header',
store: store,
methods: {
save: function() {
return this.$store.commit("save");
},
load: function() {
return this.$store.commit("load");
},
getCount: function() {
return this.$store.getters.listFilteredSorted.length;
}
}
})
var products = new Vue({
el: '#products',
store: store,
methods: {
listFilteredSorted: function() {
return this.$store.getters.listFilteredSorted;
}
}
})
// 10.1. Component product
Vue.component('product', {
props: ['obj'],
store: store,
template: '#template1',
methods: {
setEditObj: function (obj) {
this.$store.commit("setEditObj",obj);
$('.mini.modal').modal('show');
},
Delete: function (id) {
this.$store.commit("Delete",id);
}
}
})
var New = new Vue({
el: '#new',
store: store,
methods: {
getDisabled: function() {
return this.$store.state.filter.length == 0
},
add: function() {
this.$store.commit("add");
this.$refs.searchAndCreateName.value = "";
},
keyUp: function(event) {
return this.$store.commit("filterKeyUp",event.target.value);
}
}
})
var edit = new Vue({
el: '#edit',
store: store,
methods: {
editOk: function(event) {
this.$store.commit("editOk");
return this.close();
},
getEditing: function() {
return this.$store.state.editing;
},
keyUp: function(event) {
return this.$store.commit("editingKeyUp",event.target.value);
},
close: function(event) {
return $('.mini.modal').modal('hide');
}
}
})
var express = require("express");
var app = express();
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/js'));
app.use(express.json());
const DEFAULT_PRODUCTS = 4;
const SERVERPORT = 80;
var model = {
"title": "products",
"list": [],
"filter": "",
"editing": { "active": false, "id": "", "name": ""}
}
function initItems(n) {
for (i = 1; i <= n; i++)
model.list.push({
"id": i,
"name": "product " + i
})
}
app.post('/products/:action',function(req,res) {
switch (req.params.action) {
case "load":
res.send(model);
return;
case "save":
res.send("ok");
model = req.body;
return;
default:
}
});
app.listen(SERVERPORT);
initItems(DEFAULT_PRODUCTS);