Vue.js auto update computed - javascript

I am a beginner to Vue.js. I have 2 components, one as rows and the second one is the sum of all rows above. The problem is that the sums are not updated automatically, even if they are set as computed.
Find below my code :
let s = []
Vue.component('subitem-row', {
props: ['subitem', 'crt', 'si'],
template: `
<tr>
<td>
<div class="form-group" v-if="crt == si">
<label>EPE</label>
<input v-model="subitem.eprice" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.eprice}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si" #change="parseExpresion(); calculateSubitem();">
<label>Anzahl</label>
<input v-model="subitem.qtytext">
</div>
<span v-else>{{subitem.qtytext}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Faktor</label>
<input v-model="subitem.factor" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.factor}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>EK</label>
<input v-model="subitem.tp1" readonly>
</div>
<span v-else>{{subitem.tp1}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Marge</label>
<input v-model="subitem.margin" readonly>
</div>
<span v-else>{{subitem.margin}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>VK</label>
<input v-model="subitem.tp2" readonly>
</div>
<span v-else>{{subitem.tp2}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Rabatt</label>
<input v-model="subitem.discount" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.discount}}</span>
</td>
</tr>
`,
methods: {
calculateSubitem: function() {
if(this.subitem.hasOwnProperty('eprice') && !isNaN(this.subitem.eprice)) {
if(!this.subitem.hasOwnProperty('factor') || isNaN(this.subitem.factor))
this.subitem.factor = 1
if(!this.subitem.hasOwnProperty('discount') || isNaN(this.subitem.discount))
this.subitem.discount = 0
if(!this.subitem.hasOwnProperty('qty') || isNaN(this.subitem.qty))
this.subitem.qty = 0
let discount = 1 - (parseFloat(this.subitem.discount.toString().split(',').join('.')) / 100),
margin = 0
this.subitem.dprice = (this.subitem.eprice.split(',').join('.') * discount)
this.subitem.tp1 = (this.subitem.dprice * this.subitem.qty * this.subitem.factor)
this.subitem.margin = (this.subitem.tp1 * (parseFloat(margin) / 100))
this.subitem.tp2 = (this.subitem.tp1 + this.subitem.margin)
this.$forceUpdate()//{TODO} - find an alternative to $forceUpdate
}
},
parseExpresion: function() {
this.subitem.qty = parseFloat(this.subitem.qtytext.split(',').join('.')) || 0
this.$nextTick(function () {
this.calculateSubitem()
})
}
},
})
Vue.component('subitem-row-sum', {
props: ['sisum'],
template: `
<tr>
<td colspan="3">SUM</td>
<td>
<span>{{sisum.tp1}}</span>
</td>
<td>
<span>{{sisum.margin}}</span>
</td>
<td>
<span>{{sisum.tp2}}</span>
</td>
<td></td>
</tr>
`,
})
Vue.component('html-textarea',{
template: `<div class="html-textarea" contenteditable="true" #input="updateHTML" rows="3"></div>`,
props: ['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML)
}
}
})
const app = new Vue({
el: '#app',
data: {
obj: s, // main object for loading
ii: 0, // items index
si: 0, // subitems index
},
computed: {
items: function() {
return this.obj
},
row: function() {
if(!this.items.length)
this.items.push({})
return this.items[this.ii]
},
subitems: function() {
if(!this.row.hasOwnProperty('subitems'))
this.row.subitems = [{}]
return this.row.subitems
},
srow: function() {
if(!this.subitems.length)
this.subitems.push({})
return this.subitems[this.si]
},
sisum: function() {
let sisum = {
tp1: 0,
tp2: 0,
margin: 0
}
this.subitems.forEach(si => {
sisum.tp1 += si.tp1 || 0
sisum.tp2 += si.tp2 || 0
sisum.margin += si.margin || 0
})
return sisum
}
},
methods: {
setIi: function(i) {
this.ii = i
},
setSi: function(i) {
this.si = i
},
addItemRow: function() {
this.items.push({})
this.setIi(this.items.length - 1)
this.$nextTick(function () {
document.getElementById('items').scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"})
})
},
addSubitemRow: function() {
this.subitems.push({})
this.setSi(this.subitems.length - 1)
this.$nextTick(function () {
document.getElementById('subitems').scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"})
})
},
},
})
body {
padding: 0;
margin: 0;
font-size: 14px;
font-family: 'Courier New', monotype;
}
.tables-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.table-container {
height: calc(50% - 2px);
overflow: auto;
margin: 0 0 30px 0;
background: #eee
}
.table-container td {
padding: 3px 6px;
width: 60px;
height: 30px;
border-bottom: 1px solid black;
vertical-align: middle;
font-size: 1.1rem;
}
.html-textarea-container {
position: relative;
}
.html-textarea {
background: white;
border: 1px solid black;
}
.btn-group-edit {
position: absolute;
right: 2px;
bottom: 2px;
/*display: none;*/
z-index: 1;
}
/*.html-textarea:hover + .btn-group-edit,
.html-textarea:focus + .btn-group-edit {
display: block;
}*/
.table-container .form-group > input,
.table-container .form-group > textarea,
.table-container .form-group > select,
.html-textarea {
width: calc(200px - 12px);
height: calc(30px - 6px);
font-size: 1.1rem;
font-family: 'Courier New', monotype;
word-wrap: break-word;
}
.table-container .form-group > textarea,
.html-textarea {
width: calc(600px - 12px);
}
/*.table-container .form-group > textarea:focus,
.html-textarea {
height: calc(150px - 6px);
}*/
.btn-add {
width: 30px;
height: 30px;
padding: 4px 0;
}
<div id="app">
<div class="tables-container">
<div class="table-container table-subitems">
<table id="subitems">
<tr is="subitem-row" v-for="(subitem, i) in subitems" v-bind:subitem="subitem" v-bind:key="i" v-bind:crt="i" v-bind:si="si" v-on:click.native="setSi(i)"></tr>
<tr is="subitem-row-sum" v-bind:sisum="sisum"></tr>
</table>
</div>
<button class="btn-add" v-on:click="addSubitemRow">+</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Adding numbers to "EPE" and "Anzahl" fields the others are automatically filled, but the sums not. What is my mistake here? Being a computed property, I was thinking that it will be reactive and update itself, based on the current set of subitems.
Thank you.

Welcome to Stack Overflow. I've made an attempt at fixing your code, tbh I found it really difficult to understand and I think you're overusing computed properties, e.g.
items: function() {
return this.obj
},
You might as well just reference this.obj instead of this.items as you're over complicating your code.
Your main problem was initialising the data, Since Vue doesn’t allow dynamically adding root-level reactive properties, you have to initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value Declaring Reactive Properties
So I initialised s like this:
let s = [{
subitems: [{
eprice: "0",
factor: 0,
discount: 0,
qty: 0,
dprice: 0,
tp1: 0,
margin: 0,
tp2: 0
}]
}];
let s = [{
subitems: [{
eprice: "0",
factor: 0,
discount: 0,
qty: 0,
dprice: 0,
tp1: 0,
margin: 0,
tp2: 0
}]
}];
Vue.component('subitem-row', {
props: ['subitem', 'crt', 'si'],
template: `
<tr>
<td>
<div class="form-group" v-if="crt == si">
<label>EPE</label>
<input v-model="subitem.eprice" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.eprice}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si" #change="parseExpresion(); calculateSubitem();">
<label>Anzahl</label>
<input v-model="subitem.qtytext">
</div>
<span v-else>{{subitem.qtytext}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Faktor</label>
<input v-model="subitem.factor" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.factor}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>EK</label>
<input v-model="subitem.tp1" readonly>
</div>
<span v-else>{{subitem.tp1}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Marge</label>
<input v-model="subitem.margin" readonly>
</div>
<span v-else>{{subitem.margin}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>VK</label>
<input v-model="subitem.tp2" readonly>
</div>
<span v-else>{{subitem.tp2}}</span>
</td>
<td>
<div class="form-group" v-if="crt == si">
<label>Rabatt</label>
<input v-model="subitem.discount" #change="calculateSubitem();">
</div>
<span v-else>{{subitem.discount}}</span>
</td>
</tr>
`,
methods: {
calculateSubitem: function() {
if (this.subitem.hasOwnProperty('eprice') && !isNaN(this.subitem.eprice)) {
if (!this.subitem.hasOwnProperty('factor') || isNaN(this.subitem.factor))
this.subitem.factor = 1
if (!this.subitem.hasOwnProperty('discount') || isNaN(this.subitem.discount))
this.subitem.discount = 0
if (!this.subitem.hasOwnProperty('qty') || isNaN(this.subitem.qty))
this.subitem.qty = 0
let discount = 1 - (parseFloat(this.subitem.discount.toString().split(',').join('.')) / 100),
margin = 0
this.subitem.dprice = (this.subitem.eprice.split(',').join('.') * discount)
this.subitem.tp1 = (this.subitem.dprice * this.subitem.qty * this.subitem.factor)
this.subitem.margin = (this.subitem.tp1 * (parseFloat(margin) / 100))
this.subitem.tp2 = (this.subitem.tp1 + this.subitem.margin)
this.$forceUpdate() //{TODO} - find an alternative to $forceUpdate
}
},
parseExpresion: function() {
this.subitem.qty = parseFloat(this.subitem.qtytext.split(',').join('.')) || 0
this.$nextTick(function() {
this.calculateSubitem()
})
}
},
})
Vue.component('subitem-row-sum', {
props: ['sisum'],
template: `
<tr>
<td colspan="3">SUM</td>
<td>
<span>{{sisum.tp1}}</span>
</td>
<td>
<span>{{sisum.margin}}</span>
</td>
<td>
<span>{{sisum.tp2}}</span>
</td>
<td></td>
</tr>
`,
})
Vue.component('html-textarea', {
template: `<div class="html-textarea" contenteditable="true" #input="updateHTML" rows="3"></div>`,
props: ['value'],
mounted: function() {
this.$el.innerHTML = this.value;
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML)
}
}
})
const app = new Vue({
el: '#app',
data: {
obj: s, // main object for loading
ii: 0, // items index
si: 0, // subitems index
},
computed: {
items: function() {
return this.obj
},
row: function() {
if (!this.items.length)
this.items.push({})
return this.items[this.ii]
},
subitems: function() {
return this.row.subitems
},
srow: function() {
if (!this.subitems.length)
this.subitems.push({})
return this.subitems[this.si]
},
sisum: function() {
debugger;
let sisum = {
tp1: 0,
tp2: 0,
margin: 0
}
this.subitems.forEach(si => {
sisum.tp1 += si.tp1 || 0
sisum.tp2 += si.tp2 || 0
sisum.margin += si.margin || 0
})
return sisum;
}
},
methods: {
setIi: function(i) {
this.ii = i
},
setSi: function(i) {
this.si = i
},
addItemRow: function() {
this.items.push({})
this.setIi(this.items.length - 1)
this.$nextTick(function() {
document.getElementById('items').scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
})
})
},
addSubitemRow: function() {
this.subitems.push({})
this.setSi(this.subitems.length - 1)
this.$nextTick(function() {
document.getElementById('subitems').scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
})
})
},
},
})
body {
padding: 0;
margin: 0;
font-size: 14px;
font-family: 'Courier New', monotype;
}
.tables-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.table-container {
height: calc(50% - 2px);
overflow: auto;
margin: 0 0 30px 0;
background: #eee
}
.table-container td {
padding: 3px 6px;
width: 60px;
height: 30px;
border-bottom: 1px solid black;
vertical-align: middle;
font-size: 1.1rem;
}
.html-textarea-container {
position: relative;
}
.html-textarea {
background: white;
border: 1px solid black;
}
.btn-group-edit {
position: absolute;
right: 2px;
bottom: 2px;
/*display: none;*/
z-index: 1;
}
/*.html-textarea:hover + .btn-group-edit,
.html-textarea:focus + .btn-group-edit {
display: block;
}*/
.table-container .form-group>input,
.table-container .form-group>textarea,
.table-container .form-group>select,
.html-textarea {
width: calc(200px - 12px);
height: calc(30px - 6px);
font-size: 1.1rem;
font-family: 'Courier New', monotype;
word-wrap: break-word;
}
.table-container .form-group>textarea,
.html-textarea {
width: calc(600px - 12px);
}
/*.table-container .form-group > textarea:focus,
.html-textarea {
height: calc(150px - 6px);
}*/
.btn-add {
width: 30px;
height: 30px;
padding: 4px 0;
}
<div id="app">
<div class="tables-container">
<div class="table-container table-subitems">
<table id="subitems">
<tr is="subitem-row" v-for="(subitem, i) in subitems" v-bind:subitem="subitem" v-bind:key="i" v-bind:crt="i" v-bind:si="si" v-on:click.native="setSi(i)"></tr>
<tr is="subitem-row-sum" v-bind:sisum="sisum"></tr>
</table>
</div>
<button class="btn-add" v-on:click="addSubitemRow">+</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Related

How do I add hover effects over buttons on Vue.js 3?

I am building a calculator to help practice learning Vue.js 3 (I am new to vue). I have got the basic functionalities down but I am trying to figure out how to add a hover animation over the buttons. If possible I am trying to make a different hover color between the buttons in white and buttons in orange. Any help would be appreciated
Code:
<div class="calculator">
<div class="display">{{ current || '0'}}</div>
<div #click="clear" class="btn">C</div>
<div #click="sign" class="btn">+/-</div>
<div #click="percent" class="btn">%</div>
<div #click="divide" class="operator">÷</div>
<div #click="append('7')" class="btn">7</div>
<div #click="append('8')" class="btn">8</div>
<div #click="append('9')" class="btn">9</div>
<div #click="multiply" class="operator">x</div>
<div #click="append('4')" class="btn">4</div>
<div #click="append('5')" class="btn">5</div>
<div #click="append('6')" class="btn">6</div>
<div #click="minus" class="operator">-</div>
<div #click="append('1')" class="btn">1</div>
<div #click="append('2')" class="btn">2</div>
<div #click="append('3')" class="btn">3</div>
<div #click="plus" class="operator">+</div>
<div #click="append('0')" class="zero">0</div>
<div #click="dot" class="btn">.</div>
<div #click="equal" class="operator">=</div>
</div>
</template>
<script>
export default {
data() {
return {
previous: null,
current: '',
operator: null,
operatorClicked: false,
hover: false
}
},
methods: {
clear() {
this.current = '';
},
sign() {
this.current = this.current.charAt(0) === '-' ?
this.current.slice(1) : `-${this.current}`;
},
percent() {
this.current = `${parseFloat(this.current) / 100}`;
},
append(number) {
if (this.operatorClicked) {
this.current = '';
this.operatorClicked = false;
}
this.current = `${this.current}${number}`;
},
dot() {
if (this.current.indexOf('.') === -1) {
this.append('.')
}
},
setPrevious() {
this.previous = this.current;
this.operatorClicked = true;
},
plus() {
this.operator = (a,b) => a + b;
this.setPrevious();
},
minus() {
this.operator = (a,b) => a - b;
this.setPrevious();
},
multiply() {
this.operator = (a,b) => a * b;
this.setPrevious();
},
divide() {
this.operator = (a,b) => a / b;
this.setPrevious();
},
equal() {
this.current = `${this.operator(
parseFloat(this.current),
parseFloat(this.previous)
)}`;
this.previous = null;
}
}
}
</script>
<style scoped>
.calculator {
margin: 0 auto;
width: 400px;
font-size: 40px;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(50px, auto);
}
.display {
grid-column: 1 / 5;
background-color: black;
color: white;
}
.zero {
grid-column: 1 / 3;
border: 1px solid black;
}
.btn {
background-color: white;
border: 1px solid black;
}
.operator {
background-color: orange;
color: white;
border: 1px solid black;
}
</style>
You can use the :hover selector pseudo class, no need to involve js/vue for that
ie:
.btn:hover {
background-color: peach;
}
.operator:hover {
background-color: lavender;
}
Yes, just with hover on btns you can achieve this, no need vue or js
.btn:hover {
background-color: #cac8c3;
}
.operator:hover {
background-color: #6f4d00;
}
Exmaple in this codepen https://codepen.io/JavierSR/pen/LYQdjwY

Restoring GridstackJS Panels with Content (e.g. Highcharts)

I am building a Dashboard where you are able to add, delete, move and resize Panels within a grid using GridstackJS. I am filling these Panels with different things. In this Example I am using a Highchart. For saving and restoring the position and Size of the Panels I use the standard serialization of GridstackJS.
My problem now is to save and restore the Content inside the Panels.
Is there an elegant way to save the Content and apply it to the correct Panel when restoring?
JSFiddle
HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gridstack#0.5.5/dist/gridstack.css" />
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/gridstack#0.5.5/dist/gridstack.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/gridstack#0.5.5/dist/jquery-ui.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/gridstack#0.5.5/dist/gridstack.jQueryUI.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<button id="save-grid" class="btn btn-primary">Save Grid</button>
<button id="load-grid" class="btn btn-primary">Load Grid</button>
<button id="delete-grid" class="btn btn-primary">Delete Grid</button>
<div class="row">
<div class="col-sm-12">
<div class="grid-stack ui-droppable">
<div class="dragbox grid-stack-item ui-draggable ui-resizable" data-gs-id="draggable">
<h2 class="dragbox-header">Chart 1</h2>
<div class="dragbox-content">
<div class="text-center"> Item 1</div>
</div>
</div>
<div class="dragbox grid-stack-item ui-draggable ui-resizable" data-gs-id="draggable">
<h2 class="dragbox-header">Chart 2</h2>
<div class="dragbox-content"></div>
</div>
<div class="dragbox grid-stack-item ui-draggable ui-resizable" data-gs-id="draggable" data-gs-width="4" data-gs-height="4">
<h2 class="dragbox-header" id="testChartHeader">Chart 3</h2>
<div class="text-center" id="testChart"></div>
</div>
</div>
</div>
</div>
CSS
.dragbox {
margin: 5px 2px 20px;
background: #fff;
position: absolute;
border: 1px solid #ddd;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.dragbox-header {
margin: 0;
font-size: 12px;
padding: 5px;
background: #f0f0f0;
color: #000;
border-bottom: 1px solid #eee;
font-family: Verdana;
cursor: move;
position: relative;
}
.dragbox-content {
display: block;
background: #fff;
margin: 5px;
font-family: 'Lucida Grande', Verdana;
font-size: 0.8em;
line-height: 1.5em;
}
#testChart {
height: 200px;
}
.placeholder {
background: lightgray;
border: 1px dashed #ddd;
border-radius: 5px;
}
JavaScript
$(function() {
var options = {
draggable: {handle: '.dragbox-header', scroll: false, appendTo: 'body'},
placeholderClass: "placeholder",
acceptWidgets: true,
cellHeight: 60
};
$('.grid-stack').gridstack(options);
new function () {
this.serializedData = [
];
this.grid = $('.grid-stack').data('gridstack');
this.loadGrid = function () {
this.grid.removeAll();
var items = GridStackUI.Utils.sort(this.serializedData);
items.forEach(function (node, i) {
this.grid.addWidget($('<div class="grid-stack-item ui-draggable ui-resizable" data-gs-id="draggable"><div class="dragbox grid-stack-item-content ui-draggable-handle"><div class="dragbox-header">Chart ' + (i + 1) + '</div></div></div>'), node);
}, this);
return false;
}.bind(this);
this.saveGrid = function () {
this.serializedData = $('.grid-stack > .grid-stack-item').map(function (i, el) {
el = $(el);
var node = el.data('_gridstack_node');
return {
x: node.x,
y: node.y,
width: node.width,
height: node.height
};
}).toArray();
return false;
}.bind(this);
this.clearGrid = function () {
this.grid.removeAll();
return false;
}.bind(this);
$('#save-grid').click(this.saveGrid);
$('#load-grid').click(this.loadGrid);
$('#delete-grid').click(this.clearGrid);
};
});
var chart = Highcharts.chart('testChart', {
chart: {
animation: false,
type: 'bar'
},
plotOptions: {
series: {
animation: false,
}
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
});
$('.grid-stack').on('change', function(event, items) {
var chartContainer = chart.renderTo;
$(chartContainer).css(
'height',
$(chartContainer.parentElement).height() - $('#testChartHeader').height()
);
chart.reflow();
});
You need to create a chart again in the loadGrid function:
this.loadGrid = function() {
this.grid.removeAll();
var items = GridStackUI.Utils.sort(this.serializedData);
items.forEach(function(node, i) {
this.grid.addWidget($('<div class="grid-stack-item ui-draggable ui-resizable" data-gs-id="draggable"><div class="dragbox grid-stack-item-content ui-draggable-handle"><div class="dragbox-header">Chart ' + (i + 1) + '</div>' + (i === 2 ? '<div id="testChart"></div>' : '') + '</div></div>'), node);
}, this);
chart = createChart();
return false;
}.bind(this);
Live demo: https://jsfiddle.net/BlackLabel/pjy8b950/

Vue.js Battle - confirm box overrides reset function

i'm currently working through Udemy's Vue.js tutorial. I've reached the section where you are building a battle web app game. After finishing it, I decided to practice my refactoring and came across this bug.
When you click the attack buttons and then the confirm box comes up to ask if you want to play again, it seems to add one extra item in my log array instead of resetting the game fully.
I'm suspecting it is to do with pressing the attack buttons too quickly, and then the confirm box comes up before running an addToLog() and then it runs it afterwards.
Or it could be my bad code. lol
Note that I know that clicking cancel on the confirm box also comes up with bugs too.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Monster Slayer</title>
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="css/foundation.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<section class="row">
<div class="small-6 columns">
<h1 class="text-center">YOU</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: playerHealth + '%'}">
{{ playerHealth }}
</div>
</div>
</div>
<div class="small-6 columns">
<h1 class="text-center">BADDY</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: computerHealth + '%'}">
{{ computerHealth }}
</div>
</div>
</div>
</section>
<section class="row controls" v-if="!isRunning">
<div class="small-12 columns">
<button id="start-game" #click="startGame">START GAME</button>
</div>
</section>
<section class="row controls" v-else>
<div class="small-12 columns">
<button id="attack" #click="attack">ATTACK</button>
<button id="special-attack" #click="specialAttack">SPECIAL ATTACK</button>
<button id="heal" #click="heal">HEAL</button>
<button id="restart" #click="restart">RESTART</button>
</div>
</section>
<section class="row log" v-if="turns.length > 0">
<div class="small-12 columns">
<ul>
<li v-for="turn in turns" :class="{'player-turn': turn.isPlayer, 'monster-turn': !turn.isPlayer}">
{{ turn.text }}
</li>
</ul>
</div>
</section>
</div>
<script src="app.js"></script>
</body>
</html>
css/app.css
.text-center {
text-align: center;
}
.healthbar {
width: 80%;
height: 40px;
background-color: #eee;
margin: auto;
transition: width 500ms;
}
.controls,
.log {
margin-top: 30px;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
box-shadow: 0px 3px 6px #ccc;
}
.turn {
margin-top: 20px;
margin-bottom: 20px;
font-weight: bold;
font-size: 22px;
}
.log ul {
list-style: none;
font-weight: bold;
text-transform: uppercase;
}
.log ul li {
margin: 5px;
}
.log ul .player-turn {
color: blue;
background-color: #e4e8ff;
}
.log ul .monster-turn {
color: red;
background-color: #ffc0c1;
}
button {
font-size: 20px;
background-color: #eee;
padding: 12px;
box-shadow: 0 1px 1px black;
margin: 10px;
}
#start-game {
background-color: #aaffb0;
}
#start-game:hover {
background-color: #76ff7e;
}
#attack {
background-color: #ff7367;
}
#attack:hover {
background-color: #ff3f43;
}
#special-attack {
background-color: #ffaf4f;
}
#special-attack:hover {
background-color: #ff9a2b;
}
#heal {
background-color: #aaffb0;
}
#heal:hover {
background-color: #76ff7e;
}
#restart {
background-color: #ffffff;
}
#restart:hover {
background-color: #c7c7c7;
}
app.js
new Vue({
el: app,
data: {
playerHealth: 100,
computerHealth: 100,
isRunning: false,
turns: [],
},
methods: {
startGame: function() {
this.isRunning = true;
this.playerHealth = 100;
this.computerHealth = 100;
this.clearLog();
},
attackController: function(attacker, maxRange, minRange) {
let receiver = this.setReceiver(attacker);
let damage = 0;
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
if (attacker === 'computer') {
damage = this.randomDamage(maxRange, minRange);
this.playerHealth -= damage;
}
this.addToLog(attacker, receiver, damage);
if (this.checkWin()) {
return;
}
},
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
specialAttack: function() {
this.attackController('player', 30, 5);
this.attackController('computer', 30, 5);
},
heal: function() {
if (this.playerHealth <= 90) {
this.playerHealth += 10;
} else {
this.playerHealth = 100;
}
this.turns.unshift({
isPlayer: true,
text: 'Player heals for ' + 10,
});
},
randomDamage: function(max, min) {
return Math.floor(Math.random() * max, min);
},
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
}
return false;
},
alertBox: function(message) {
if (confirm(message)) {
this.isRunning = false;
this.startGame();
} else {
this.isRunning = false;
}
return true;
},
restart: function() {
this.isRunning = false;
this.startGame();
},
addToLog: function(attacker, receiver, damage) {
this.turns.unshift({
isPlayer: attacker === 'player',
text: attacker + ' hits ' + receiver + ' for ' + damage,
});
},
clearLog: function() {
this.turns = [];
},
setReceiver: function(attacker) {
if (attacker === 'player') {
return 'computer';
} else {
return 'player';
}
},
damageOutput: function(attacker, health) {
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
},
},
});
Github repo is here if you prefer that. Thanks!
Your attack (and specialAttack) function attacks for both players:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
Currently, it is checking for win at every attackController call. So when the first attacker (player) wins, the game resets AND the second player attacks.
So, my suggestion, move the checkWin out of the attackController into the attack functions:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
this.checkWin();
},
The same to specialAttack.
Code/JSFiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/10/
Note, when the player wins, in the code above, the computer will still "strike back", even though the game is over. If you want to halt that, make checkWin return if the game is over:
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
return true;
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
return true;
}
return false;
},
And add an if to attack (and specialAttack):
attack: function() {
this.attackController('player', 10, 3);
if (this.checkWin()) return;
this.attackController('computer', 10, 3);
this.checkWin();
},
Updated fiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/13/

how to create a muticolumn combo box in angular js

I want to have a two fields name and shade. these two fields should be shown as
grid when i click a dropdown. and when i select a row from the grid only name should be binded to dropdown.some thing like this in the below link
https://demos.devexpress.com/ASPxEditorsDemos/ASPxComboBox/MultiColumn.aspx
below is my code
<script>
angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.colors = [
{name:'black', shade:'dark'},
{name:'white', shade:'light'},
{name:'red', shade:'dark'},
{name:'blue', shade:'dark'},
{name:'yellow', shade:'light'}
];
$scope.myColor = $scope.colors[2]; // red
}]);
</script>
<div ng-controller="ExampleController">
<span >
<select ng-model="myColor" ng-options="color.name for color in colors">
<option value="">-- choose color --</option>
</select>
</span>
</div>
You can try with this new directive :
Note: This answer is based on your refrence link.
(function(){
var app = angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.colors = [
{ name: 'black', shade: 'dark' },
{ name: 'white', shade: 'light' },
{ name: 'red', shade: 'dark' },
{ name: 'blue', shade: 'dark' },
{ name: 'yellow', shade: 'light' }
];
$scope.myColor = $scope.colors[2];
// red
}]);
app.directive('dropdown', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
list: '=dropdown',
ngModel: '=',
selectedKey: '=',
dropHeader: '='
},
templateUrl: '/tpl.html',
replace: true,
link: function(scope, elem, attrs, ngModel) {
scope.head = false;
scope.tdArry = [];
if (scope.dropHeader == true) scope.head = true;
if (Array.isArray(scope.list)) {
var p = scope.list.map(function(_item) {
var keys = Object.keys(_item);
if (keys.length > 0) {
if (scope.tdArry.length == 0) scope.tdArry = keys;
else {
var j = keys.map(function(_k) {
if (scope.tdArry.indexOf(_k) == -1)
scope.tdArry.push(_k);
});
}
return;
} else return;
})
} else {
console.log('Directive Expecting an array of values ')
}
scope.$watch('ngModel', function() {
scope.selected = ngModel.$modelValue;
});
scope.update = function(thing) {
ngModel.$setViewValue(thing);
ngModel.$render();
};
scope.getUpdatedVal = function() {
var selectedKey = scope.selectedKey;
if (selectedKey) {
if (scope.ngModel[selectedKey]) return scope.ngModel[selectedKey];
else {
var k = Object.keys(scope.ngModel);
return scope.ngModel[k[0]];
}
} else {
var k = Object.keys(scope.ngModel);
return scope.ngModel[k[0]]
}
}
},
}
})
})()
.title {
padding: 5px 10px;
border: 1px solid #ccc;
cursor: pointer;
}
.title span {
width: 100%;
display: block;
position: relative;
}
.title span:after {
width: 0;
height: 0;
border: 5px solid transparent;
border-bottom-width: 5px;
border-bottom-style: solid;
border-top-color: transparent;
border-bottom-color: transparent;
border-bottom: none;
border-top-color: #201e1d;
content: '';
vertical-align: middle;
display: inline-block;
position: absolute;
right: 2px;
top: 8px
}
.drop-table {
width: 100%;
border-width: 0;
border-collapse: collapse;
border-spacing: 0;
}
.drop-table th {
background: #eaeef2;
}
.drop-table th,
.drop-table td {
text-align: left;
padding: 5px 7px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js"></script>
<div ng-app="selectExample">
<div ng-controller="ExampleController">
<!-- <div> -->
<div dropdown="colors" drop-header='true' selected-key="'name'" ng-model="myColor"></div>
</div>
<script type="text/ng-template" id="/tpl.html">
<div ng-click="open=!open">
<div class="title">
<span>{{getUpdatedVal()}}</span>
</div>
<table class="drop-table" ng-hide="!open">
<tr ng-if="head">
<th ng-repeat="h in tdArry">{{h}}</th>
</tr>
<tr ng-repeat="li in list" ng-click="update(li)">
<td ng-repeat="d in tdArry">{{li[d]}}</td>
</tr>
</table>
</div>
</script>
</div>

Variable is null - javascript

I am trying to use the demo kendo ui dropdownlist. For some reason when I create my variable "dropdownlist" to set its datasource, the variable is null Can anyone see what needs to be changed?
Here is the demo of it being used http://demos.telerik.com/kendo-ui/dropdownlist/index
function getcombo() {
var data = getVillages();
console.log(data);
var dropdownlist = $("#color").data("kendoDropDownList");
console.log(dropdownlist); //error here
dropdownlist.setDataSource(data);
};
In my html I have:
<div id="example">
<div id="cap-view" class="demo-section k-header">
<h2>Customize your Kendo Cap</h2>
<div id="cap" class="black-cap"></div>
<div id="options">
<h3>Cap Color</h3>
<input id="color" value="1" />
<h3>Cap Size</h3>
<select id="size">
<option>S - 6 3/4"</option>
<option>M - 7 1/4"</option>
<option>L - 7 1/8"</option>
<option>XL - 7 5/8"</option>
</select>
<button class="k-button" id="get">Customize</button>
</div>
</div>
<style scoped>
.demo-section {
width: 460px;
height: 300px;
}
.demo-section h2 {
text-transform: uppercase;
font-size: 1em;
margin-bottom: 30px;
}
#cap {
float: left;
width: 242px;
height: 225px;
margin: 20px 30px 30px 0;
background-image: url('../content/web/dropdownlist/cap.png');
background-repeat: no-repeat;
background-color: transparent;
}
.black-cap {
background-position: 0 0;
}
.grey-cap {
background-position: 0 -225px;
}
.orange-cap {
background-position: 0 -450px;
}
#options {
padding: 1px 0 30px 30px;
}
#options h3 {
font-size: 1em;
font-weight: bold;
margin: 25px 0 8px 0;
}
#get {
margin-top: 25px;
}
</style>
<script>
$(document).ready(function() {
var data = [
{ text: "Black", value: "1" },
{ text: "Orange", value: "2" },
{ text: "Grey", value: "3" }
];
// create DropDownList from input HTML element
$("#color").kendoDropDownList({
dataTextField: "text",
dataValueField: "value",
dataSource: data,
index: 0,
change: onChange
});
// create DropDownList from select HTML element
$("#size").kendoDropDownList();
var color = $("#color").data("kendoDropDownList");
color.select(0);
var size = $("#size").data("kendoDropDownList");
function onChange() {
var value = $("#color").val();
$("#cap")
.toggleClass("black-cap", value == 1)
.toggleClass("orange-cap", value == 2)
.toggleClass("grey-cap", value == 3);
};
$("#get").click(function() {
alert('Thank you! Your Choice is:\n\nColor ID: '+color.value()+' and Size: '+size.value());
});
});
</script>
</div>
my guess would be "jQuery.data( element, key, value )"
normally 'null' only exists if the user sets the variable to explicitly be 'null' otherwise it would be undefined
by the way, you can console.log(arg1, arg2, etc.) with commas to log them all at once on the same line

Categories