Given a YAML file and go templates, I can assign a variable with:
{{ $foo := "bar" }}
And I can use a conditional like:
{{ if eq $foo "bar" }} jim {{ else }} bob {{ end }}
How do I combine the two to assign the result of a condition to a variable?
I've tried:
{{ $foo := "bar" }}
{{ if eq $foo "bar" }}
{{ $foo = "jim" }}
{{ else }}
{{ $foo = "bob" }}
{{ end }}
But foo remains bar
This is not possible with Go 1.10 and earlier versions, template variables (identifiers) cannot be modified (there are "workarounds", see following link). They can be re-declared / shadowed in a(n inner) block, but once you are out of the inner block, the changes will not be visible. For details and workarounds, see In a Go template range loop, are variables declared outside the loop reset on each iteration?
Note that however Go 1.11 is about to be released which will support this.
This will be a valid and working template code starting with Go 1.11:
{{ $v := "init" }}
{{ if true }}
{{ $v = "changed" }}
{{ end }}
v: {{ $v }} {{/* "changed" */}}
Related
I have a component that displays rows of data which I want to toggle to show or hide details. This is how this should look:
This is done by making the mapping the data to a new array and adding a opened property. Full working code:
<script setup>
import { defineProps, reactive } from 'vue';
const props = defineProps({
data: {
type: Array,
required: true,
},
dataKey: {
type: String,
required: true,
},
});
const rows = reactive(props.data.map(value => {
return {
value,
opened: false,
};
}));
function toggleDetails(row) {
row.opened = !row.opened;
}
</script>
<template>
<div>
<template v-for="row in rows" :key="row.value[dataKey]">
<div>
<!-- Toggle Details -->
<a #click.prevent="() => toggleDetails(row)">
{{ row.value.key }}: {{ row.opened ? 'Hide' : 'Show' }} details
</a>
<!-- Details -->
<div v-if="row.opened" style="border: 1px solid #ccc">
<div>opened: <pre>{{ row.opened }}</pre></div>
<div>value: </div>
<pre>{{ row.value }}</pre>
</div>
</div>
</template>
</div>
</template>
However, I do not want to make the Array deeply reactive, so i tried working with ref to only make opened reactive:
const rows = props.data.map(value => {
return {
value,
opened: ref(false),
};
});
function toggleDetails(row) {
row.opened.value = !row.opened.value;
}
The property opened is now fully reactive, but the toggle doesn't work anymore:
How can I make this toggle work without making the entire value reactive?
The problem seems to come from Vue replacing the ref with its value.
When row.opened is a ref initialized as ref(false), a template expression like this:
{{ row.opened ? 'Hide' : 'Show' }}
seems to be interpreted as (literally)
{{ false ? 'Hide' : 'Show' }}
and not as expected as (figuratively):
{{ row.opened.value ? 'Hide' : 'Show' }}
But if I write it as above (with the .value), it works.
Same with the if, it works if I do:
<div v-if="row.opened.value">
It is interesting that the behavior occurs in v-if and ternaries, but not on direct access, i.e. {{ rows[0].opened }} is reactive but {{ rows[0].opened ? "true" : "false" }} is not. This seems to be an issue with Vue's expression parser. There is a similar problem here.
this is the example of code
{{ __('Remember Me') }}
I'm quite new to coding (less than 3months old) and I'm currently trying to learn vue.
I'm trying out this simple exercise of doing a basic shopping cart and I want to get the total of all the product amounts. Here is my code:
HTML
<template>
<div class="product" #click="isMilkshown = true">{{ productList[0].name }} $ {{ productList[0].amount }}</div>
<div class="product" #click="isFishshown = true">{{ productList[1].name }} $ {{ productList[1].amount }}</div>
<div class="product" #click="isLettuceshown = true">{{ productList[2].name }} $ {{ productList[2].amount }}</div>
<div class="product" #click="isRiceshown = true">{{ productList[3].name }} $ {{ productList[3].amount }}</div>
<!-- Cart -->
<div class="main-cart">
<div>Cart</div>
<div class="main-cart-list" v-for="product in productList" :key="product">
<div v-if="showProduct(product.name)">{{ product.name }} $ {{ product.amount }}</div>
</div>
<div>Total: 0</div>
</div>
</template>
JS
export default {
data() {
return {
productList: [
{ name: "Milk", amount: 10 },
{ name: "Fish", amount: 20 },
{ name: "Lettuce", amount: 5 },
{ name: "Rice", amount: 2.5 }
],
isMilkshown: false,
isFishshown: false,
isLettuceshown: false,
isRiceshown: false
}
},
methods: {
showProduct(name) {
if (name === "Milk" && this.isMilkshown === false) {
return false
} else if (name === "Fish" && this.isFishshown === false) {
return false
} else if (name === "Lettuce" && this.isLettuceshown === false) {
return false
} else if (name === "Rice" && this.isRiceshown === false) {
return false
} else {
return true
}
}
}
}
I want to replace the "zero" in Total with the sum of all the product amounts when a product is clicked. Hope someone can help me, thanks!
You would use a computed function.
https://v2.vuejs.org/v2/guide/computed.html
In Vue, computed functions watch all the reactive variables referenced within them and re-run to update the returned value when any of those variables change.
Simply create a computed function that loops over each productList item and sums up the amount then returns it.
You can reference this answer to learn how to sum using reduce or for a standard example with a loop Better way to sum a property value in an array
Also, you can use a v-for loop on your
<div class="product" #click="isMilkshown = true">{{ productList[0].name }} $ {{ productList[0].amount }}</div>
component so that you don't have duplicated code:
<div v-for="item in productList" key="item.name" class="product">{{ item.name }} $ {{ item.amount }}</div>
This would create one of each of those elements for each item in your productList variable.
You would then need to re-write the click handler to be dynamic too.
Lastly, you can also convert your big if/else-if chained method into a computed function too so that you watch for changes in that.
To do that, you'd make the computed return an object like this:
{
Milk: true,
Fish: false
...
}
You can put the key as the name so that in your loop you can reference the computed property like this enabledItems[item.name] to get the true/false.
I notice that some of my computed properties don't fire when they call upon the same data. As a super-simplistic example let's say I have this:
<template v-for="item in foo" v-slot:['sameSlot`]="{sameBlah}">
{{ JSON.stringify(item) }}
</template>
<template v-for="item in bar" v-slot:['sameSlot`]="{sameBlah}">
{{ JSON.stringify(item) }}
</template>
<template v-for="item in baz" v-slot:['sameSlot`]="{sameBlah}">
{{ JSON.stringify(item) }}
</template>
...
data: () => ({
someData: [
{type:'foo', value: 1},
{type:'bar', value: 2},
{type:'baz', value: 3},
],
});
computed: {
foo() {
console.log('Hello from foo');
return this.someData.filter(f => f.type === 'foo');
},
bar() {
console.log('Hello from bar');
return this.someData.filter(f => f.type === 'bar');
},
baz() {
console.log('Hello from baz');
return this.someData.filter(f => f.type === 'baz');
},
}
If I run that, I only see console output from one of the computed methods, not all. Same with seeing results in the templates. Is this by design?
BTW I'm aware I could eliminate the computed properties by creating a method where a filter key could be passed in, but that doesn't showcase this 'issue'.
The code in the post targets the same slot 3 times, so Vue completely ignores the first 2 without running any of the code in the first two templates even once.
This is a smart feature to minimize workload because it's not sensible to target the same slot more than once to begin with. If anything, maybe there should be a warning from the compiler in this situation.
You could even have code that tries to use variables which don't exist, and there will be no compiler warning. For example:
<!-- First call: completely ignored, no error -->
<template v-for="item in notDefinedOnTheInstance" v-slot:[`sameSlot`]="{sameBlah}">
{{ JSON.stringify(item) }}
</template>
<!-- Second call: used for the slot -->
<template v-for="item in baz" v-slot:[`sameSlot`]="{sameBlah}">
{{ JSON.stringify(item) }}
</template>
Despite not existing in the instance, notDefinedOnTheInstance won't throw a warning because that whole section has been ignored. So it's for this reason that you also don't see the computeds output in the console, because they never run.
Just started with Vue so I can't get this simple thing working. All I'm trying to do is toggle a class based on a condition.
<button type="button"
class="btn dropdown-toggle"
v-bind:class="{ btn-default: (search.category != 'all') }">
{{ filterCategoryText || 'Category' }}
</button>
Firstly, as you discovered, you should probably remove the duplicate class definition. You can mix static and dynamic classes in the bound class definition. (If you leave the duplicate there it still works, though)
Then, you have the choice...
Object syntax
// property names will be in the class list if their values are truthy
:class="{
'btn-default': search.category != "all",
'btn' : true,
'dropdown-toggle' : true
}"
Array syntax
// an item in the array becomes a class in the class list
:class="[
search.category != 'all' ? 'btn-default':'',
'btn',
'dropdown-toggle'
]"
Simple expression
// if you only have one item, just use a simple expression
:class="search.category != 'all' ? 'btn-default':''"
Docs are here
You still could have used Object syntax for the class binding. I guess the code in you example didn't work because you didn't wrap object property name with quotes. Moreover, you have at least three options here. In such case I would always stick with objects if possible as it's usually more readable. Take a look:
new Vue({
el: '#app',
data: {
red: 'nope',
},
methods: {
toggle() {
this.red = this.red === 'yes' ? 'nope' : 'yes';
}
},
})
.is-bold {
font-weight: 900;
}
.is-red {
color: red;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p class="is-bold" :class="{'is-red': red === 'yes'}">
Option
</p>
<p class="is-bold" :class="red === 'yes' ? 'is-red' : ''">
Option 1
</p>
<p class="is-bold" :class="[red === 'yes' ? 'is-red' : '']">
Option 2
</p>
<button #click="toggle">Toggle class</button>
</div>
jsfiddle: https://jsfiddle.net/oniondomes/06bg516h/
Figured it:
<button type="button"
:class="[(search.category) ? '' : 'btn-default', 'btn dropdown-toggle']"
{{ filterCategoryText || 'Category' }}
</button>
try this instead of v-bind:class="{ 'btn-default': search.category != 'all' }"