Mustache issue with array of objects - javascript

So I searched for similar question and I applied their answers but it doesn't seem to work in my case. It's frustrating 'cause it should be something simple.
So, I use mustache template to render data that come from a node server with sockets.
It works fine with one object but when I try to iterate through an array of object it just render a blank page.
This is my socket, the push is just to simulate what I'm gonna do next (get a new object in the array)
let productsTab = [{
prodName: prodName,
prodPrice: prodPrice,
quantity: quantity,
quantityUnit: quantityUnit,
isQuantityManual: isQuantityManual,
hasQuantityUnit: hasQuantityUnit,
totalPrice: totalPrice
}];
productsTab.push({
prodName: prodName,
prodPrice: prodPrice,
quantity: quantity,
quantityUnit: quantityUnit,
isQuantityManual: isQuantityManual,
hasQuantityUnit: hasQuantityUnit,
totalPrice: totalPrice
});
res.render('displayProduct', productsTab);
this is the log of my array
[
{
prodName: 'name',
prodPrice: 'price',
quantity: '1',
quantityUnit: '',
isQuantityManual: false,
hasQuantityUnit: false,
totalPrice: 'price'
},
{
prodName: 'name',
prodPrice: 'price',
quantity: '1',
quantityUnit: '',
isQuantityManual: false,
hasQuantityUnit: false,
totalPrice: 'price'
}
]
And finally this is my mustache template
{{#productsTab}}
<div>
<div>
{{ prodName}}
</div>
<div>
{{{ prodPrice }}}
{{ #hasQuantityUnit }}
text
{{ /hasQuantityUnit }}
</div>
</div>
<div>
<div>
x {{ quantity }} {{ quantityUnit }}
{{ #isQuantityManual }}
(MAN)
{{ /isQuantityManual }}
</div>
<div>
= {{{ totalPrice }}}
</div>
</div>
{{/productsTab}}
It works fine with just an object (not an array) without the {{#}} loop feature so the issue must come from the array...
I need some help please

Ok, I got it. It is when I render my template
res.render('displayProduct', productsTab);
This works only when the props you pass is an object but I had to iterate through an array of object so I did this :
res.render('displayProduct', { productsTab });
Hope it will help someone that got a brain freeze like me.

Related

How to Store Data Property Value from a Specific Item in Rendered List in Vue

I'm trying create a follow button on list items in Vue. My strategy is to grab the value of a particular list item property and store it in the data object. Then use this value in a method to add it to an array in my database.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock">+follow</button>
</div>
I'm not sure how to get the value of result.symbol "into" the button element to set the value symbol in the data object below.
<script>
export default {
data() {
return {
results: [ // this is populated by an api call
{
currency: "USD"
exchangeShortName: "NYSE"
name: "International Game Technology PLC"
stockExchange: "NYSE"
symbol: "IGT"
},
{...},
...
],
symbol: "",
};
},
followStock() {
// add this.symbol to database array
},
},
};
</script>
I'm guessing there might be an easier strategy I'm overlooking as I'm still new to Vue, so any other solution that essentially allows me to fire off the value of result.symbol from any rendered result to my database would be awesome.
You can just pass the result as a parameter to your method.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result)">+follow</button>
</div>
And in your method:
methods: {
followStock(result) {
// do something with result
console.log({result});
let symbol = result.symbol;
},
}
P.S I didn't see you put your followStock() inside a methods object, but I did so in the example. https://v2.vuejs.org/v2/api/#methods
Write directly as a function call.
The vue compiler will turn followStock(result.symbol) into function(event) {followStock(result.symbol)}.
new Vue({
el: '#app',
data() {
return {
results: [
{
name: "International Game Technology PLC",
symbol: "IGT"
},
{
name: "A name",
symbol: "A symbol"
}
]
};
},
methods: {
followStock(symbol) {
console.log(symbol)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
</div>
As Nazaire mentioned you can access the results anywhere inside the child elements when using v-for.
(it works like a normal for-loop)
It's not only limited to the corresponding element (the element in which you do v-for)
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
followStock(symbol){
// you can now add symbol to db
}

Vue.Js Multiple inputs with different types

I am working on a survey with Vue. I am using an array for all the questions and an index to navigate through them and display them one at a time. I'm using different input types for each question, e.g. number, radio, text etc. For some questions I'll need more than one input. I am using v-bind to pass the type of the question.
Now the problem that I encounter is that I'll need more than one input per question, e.g. when passing radio button I only get one when I need 2+. Same for labels for the buttons. I have also realized that I'm going to need two different input types for some questions (e.g. both input number and radio).
This is my working fiddle, to give you an idea of what I'm trying to accomplish. I would like to know if this is doable with my current approach, or if I need to use components for the questions and use different templates for each of them and how I would go about doing that.
I am writing this for the second time from memory since the first time I got an error, so I apologize if I failed to mention something important. Thanks in advance!
new Vue({
el: '#quizz',
data: {
questions:[
{question: 'What is your gender?', answer: '', type: 'radio', checked: 'true', label: 'Male'},
{question:'How old are you?', answer: '', type: 'number', checked: 'false'},
{question:'How many times do you workout per week?', answer: '', type: 'number', checked: 'false'},
],
index:0
},
computed:{
currentQuestion(){
return this.questions[this.index]
}
},
methods:{
next(){
if(this.index + 1 == this.questions.length)
this.index = 0;
else
this.index++;
},
previous(){
if(this.index - 1 < 0)
this.index = this.questions.length - 1;
else
this.index--;
}
}
})
I would probably handle this by building question "type" components. For example,
const RadioQuestion = {
props:["value", "question"],
template:`
<div>
<template v-for="label in question.labels">
<input type="radio" :id="label" :value="label" v-model="picked">
<label :for="label">{{label}}</label>
<br>
</template>
</div>
`,
data(){
return {
picked: this.value
}
},
watch:{
picked(){
this.$emit("input", this.picked)
}
}
}
const NumericInputQuestion = {
props:["value", "question"],
template:`
<div>
<input type="number" v-model="internalValue" #input="onInput" :value="value" />
</div>
`,
data(){
return {
internalValue: this.value
}
},
methods:{
onInput(){this.$emit("input", this.internalValue)}
}
}
Then build your data like this.
data: {
questions:[
{question: 'What is your gender?', type: RadioQuestion, labels:["Male", "Female"], answer: null},
{question:'How old are you?', type: NumericInputQuestion, answer: null},
{question:'How many times do you workout per week?', type: NumericInputQuestion, answer: null}
],
index:0
}
Modify your template accordingly.
<div id="quizz" class="question">
<h2>
{{ currentQuestion.question }}
</h2>
<component :key="currentQuestion" :is="currentQuestion.type" :question="currentQuestion" v-model="currentQuestion.answer"></component>
Current Question Answer: {{ currentQuestion.answer }}
<div class='button' id='next'>Next</div>
<div class='button' id='prev'>Prev</div>
</div>
Here is an updated fiddle demonstrating the technique.

concatenate an ng repeat item as an object inside ng-model

I'm trying to concatenate an ng-repeat item to ng-model object. I was wondering if this possible.
so for example:
//array
$scope.array = [
{
param: 'color',
....
},
{
param: 'weight',
.....
}
]
html
<div ng-repeat="item in array">
{{ item.param }}
<input type="text" ng-model="form.name.{{ item.param }}" >
</div>
so lets say {{ item.param }} is color, the ng-model will be form.name.color.
form object will be something like this:
{
name: {
color: 'value of input',
weight: 'value of input'
}
}
How can I concatenate the item.param to the object form.name? I've been trying so many ways but no results. I trying to use $index, but don't know where to begin.
Your help will be appreciated!
Check out this fiddle.
Your ng-model should look like this:
ng-model="form.name[item.param]"

How to output html from filter inside mustache

I have a input (top right) where users can search things, when it's directive length get 3 characters it will display a list of products and highlight the matches...
Look at my code:
html
<div id="app">
<div id="header">
<div class="right"><input type="text" v-model="message" v-on:keyup="searchStart()" v-on:blur="searchLeave()"/>
<ul v-if="this.searchInput" class="product-list">
<li v-for="product in products">
{{ product.id }} - {{ product.name | highlight }} - {{ product.qtd }}</li></ul>
</div>
</div>
<div id="main">
<div id="menu">fdfds</div>
<div id="container">{{ message }}</div>
</div>
</div>
js
var search = new Vue({
el: "#app",
data: {
message: "",
searchInput: false,
products: [
{
id: 1,
name: "produto 01",
qtd: 20
},
{
id: 2,
name: "produto 02",
qtd: 40
},
{
id: 3,
name: "produto 03",
qtd: 30
},
]
},
methods: {
searchStart: function(){
if(this.message.length >= 3)
this.searchInput = true;
console.log(this.searchInput);
},
searchLeave: function(){
this.searchInput = false;
this.message = "";
console.log(this.searchInput);
}
},
filters: {
highlight: function(value){
return value.replace(search.message, '<span class=\'highlight\'>' + search.message + '</span>');
}
}
});
Here you can see a live pen: http://codepen.io/caiokawasaki/pen/dXaPyj
try to type prod inside the pen...
Is my filter correct? The way I created the filter is correct?
The main question is: How to output the HTML from my filter?
Edit/Solution
The problem in the case was codepen, there is some kind of conflict with vue, so I was not able to escape the html using {{{}}}, put the code in another editor (jsfidle) and it worked.
I'm accepting the answer given to the reward because it's right.
You'll need 3 steps here for achieve what you want:
Use triple braces {{{ }}} to display unescaped html
Filter your users by your v-model variable, in order to just show the matches
Replace the substring matching by the <span> tag
Check out the computed property filteredUsers and the filter in this working jsfiddle

Using AngularJS to get the count of a sub array

Basically I'm making a todo list with angular and I want to display the how many items have been marked as done I have an array object of Lists and each list has a collection of list items called todos like so:
[{listName: "ESSENTIALS", newTodoName:"", newTodoQ:"0", todos: [
{ taskName : "Comb" , quantity: 1, isDone : false , id: "comb" } ]},
{listName: "TOILETRIES", newTodoName:"", newTodoQ:"0", todos: [
{ taskName : "Tooth Brush" , quantity: 1, isDone : false , id: "toothbrush"},
{ taskName : "Tooth paste" , quantity: 6, isDone : false, id: "toothpaste" },
{ taskName : "Deodorant" , quantity: 3, isDone : false, id: "deodorant" }
]}];
So I have two ng-repeats.. One repeats the Lists then another inside of it prints out each list item. I have an H1 and next to the title I want to have the the items that were marked isDone as true next to the total amount of records to show how many items you have left. As you can see I started to code up a filter but I believe it's wrong I keep getting: "Syntax Error: Token 'undefined' is unexpected, expecting [}] at column null of the expression [ (list.todos |] starting at [{4}]." which I'm not really sure what that means...my fourth item is blank? Yet I have all my todos there and they are not blank. Is my filter wrong? or a better way to do this?
<div class='row' ng-repeat="list in lists">
<h1 class='centerTitle'>{{list.listName}} <span class="listCounter"> {{ (list.todos | filter:{todos: {isDone: true}}: true).length }} / {{list.todos.length}}</span></h1>
<ul ui-sortable="todoSortable" ng-model="list.todos">
<li ng-class="{taskDone: todo.isDone}" class="todoTask" ng-repeat="todo in list.todos | orderBy: 'isDone' "></li>
<div>
Can you try:
<h1 class='centerTitle'>{{list.listName}} <span class="listCounter"> {{ (list.todos | filter: {isDone: true}).length }} / {{list.todos.length}}</span></h1>

Categories

Resources