Storing an integer variable in local storage in alpine.js - javascript

I wanted to store an integer value in local storage using alpine.js
There is a button that increments the value by 1 when pressed.
Here is how I thought it should be:
<div id="greeting" class="inner" x-data="{ $store.integer: 0 }">
<button class="button bouncy" #click="$store.integer+=1" x-text="$store.integer + ' is the number'"></button>
</div>
<script>
document.addEventListener('alpine:init', () =>
Alpine.store('store', integer)
})
</script>
This didn't work. I tried some other implementations, but none of them seemed to work. I also tried not adding the $store, since it showed the integer as "undefined" when I did that.

Alpine has the persist plugin which looks like it might suit your needs.
https://alpinejs.dev/plugins/persist
<div x-data="{ count: $persist(0) }">
<button x-on:click="count++">Increment</button>
<span x-text="count"></span>
</div>

You have a couple of mistakes in the example code. The corrected version is the following:
<div id="greeting" class="inner" x-data="{}">
<button class="button bouncy" #click="$store.integer += 1" x-text="$store.integer + ' is the number'"></button>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('integer', 0)
})
</script>
When you create a new variable in the store, the first argument is the name of the variable, the second is its initial value.
In the component, you don't have to recreate the variable in the x-data attribute, since it is defined in the alpine:init hook. So you can use just an empty x-data (assuming you don't have other variables in the component).
You have a missing { after =>.

Related

apply function from methods to reverse string in paragraph in vue.js

Dears, I have tried to apply function to reverse string in paragraph text in vue.js,
I have created function to reverse words in methods called (reverseword) and added it card using :rule="reverseword()",but it does not work. your support is highly appreciated
Code:
<div class="post-box">
<span class="post-viwes">{{viwes}}</span>
<h3 class="post-title">{{title}}</h3>
<span class="post-date">{{date}}</span>
<p class="post-content">{{content}}</p>
<div class="row">
<div class = "col-sm-6 text-right">
<span class="post-author">{{author}} </span>
</div>
<div class = "col-sm-6 text-right" :rules="reverseword()">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
</div>
)
</div>
</template>
<script>
export default {
props:["viwes","title","date","content","author","category"],
name:"posts",
methods: {
reverseWord: function () {
this.category = this.category.split('').reverse().join('')
}
}};
</script>```
Your reverseWord method attempts to mutate a prop (category).
You can't mutate a prop, because the mutation would be overwritten by the first update from the parent.
If/when you do want/need to change a prop value, you have do it in the parent component which will then pass the change down to the child component, through the props binding.
Updating the parent from child can be done by
either using $emit
or by using a store, external to both the child and the parent.
If, in fact, you don't want to mutate category, you just need to be able to use its reverse inside the template, create a computed property:
computed: {
reversedCategory() {
return this.category.split('').reverse().join('');
}
}
Use it in template as you would use a normal property:
<div class = "col-sm-6 text-right" :rules="reversedCategory">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
The computed is reactive. Meaning every time category changes, reverseCategory will update accordingly.

Why nested array dynamic update is not working in my case

Hi i have a problem with nested array dynamic update as shown in below image
Here is a steps to re-produce the problem
click on Add Likes button it will add likes for eg New Apple
my default like is Mango
click on Add about button it will add next row (Mango is not appeared) click on Add Likes to see the problem.
For reducing code readability i have put helper function into a mixin file called mixin.js
Expectation: i can add any number of Add about and Add Likes(with default value Mango)
Here is my code: https://codesandbox.io/s/musing-hawking-di4d3?file=/src/App.vue
First, you don't need a nested array for the tags property.
Use:
getLikesTemplate() {
let year = this.year;
let template = {
id: this.getUniqueId(),
like: `I'm ${year} Old and like things like`,
tags: [this.getTagsTemplate("Mango")] //nesting removed
};
this.year++;
return template;
}
Secondly, in JS objects are passed by reference, so you can do this:
method:
addLikes(like) { //removed the extra code
like.tags.push(this.getTagsTemplate("New Apple"));
},
template:
...
<div style="text-align: left; display: flex">
<div> //nesting removed
<div class="tags" v-for="tag in like.tags" :key="tag.id">
{{ tag.name }}
</div>
</div> //passing the reference to the object
<button style="margin-left: 20px" #click="addLikes(like)">
Add Likes
</button>
</div>
Result img

angular: value generated using interval not reflecting in browser using {{}}

I am trying to show the value being changed after each second in the web page, but {{}} is not working.
However, I used console.log, which does show changing value.
Here is my .ts code snippet
randomValue: number;
processIncrementValue(){
this.randomValue = Math.floor((Math.random()*100)+1);
console.log("generating random number: " + this.randomValue);
}
// on this function by button press, value starts to change
start(){
console.log("generating random number: start " + this.randomValue);
this.myVar = setInterval(this.processIncrementValue, 1000);
}
.HTML file snippet
<div class="row">
<button type="button" class="btn btn-primary" (click)="start()">Start</button>
<hr>
<div class="row">
<p>my name is: {{randomValue}}</p>
</div>
</div>
Bind setInterval to scope with the ES6 arrow function () =>
this.myVar = setInterval(()=>{this.processIncrementValue()}, 1000);
When you use setInterval(function () { doThing() });, it binds this to the window object. So in the function doThing() if you have this.doSomething, your code wants to find a variable in the window object called doSomething, which doesn't exist.
When you use the arrow function, it binds this to the current scope, which is your angular component.
From: https://www.w3schools.com/js/js_arrow_function.asp
In regular functions the this keyword represented the object that called the function, which could be the window, the document, a button or whatever.
With arrow functions the this keyword always represents the object that defined the arrow function.
You could try this
.TS file
randomValue: number; // = 0 if you need to initialize it
processIncrementValue() {
this.randomValue = Math.floor((Math.random() * 100) + 1);
setTimeout(() => {
this.processIncrementValue();
}, 1000);
}
.HTML file
<div class="row">
<button type="button" class="btn btn-primary" (click)="processIncrementValue()">Start</button>
<div class="row">
<p>my name is: {{randomValue}}</p>
</div>
</div>

Cannot assign to read only property 'bEditing' of object '#<Object >' error on lwc LWC

I'm getting the above error, when trying to edit a property value of an object inside list. Please refer below code for better understanding.
HTML:
<template>
<div class="container">
<div class="slds-grid slds-gutters">
<div class="slds-col" for:each={lstQuotes} for:item="objQuote" for:index="pindex" key={objQuote.Id}>
Quote : <span>{objQuote.sQuoteName}</span><br/>
Opportunity : <span>{objQuote.sOpptName}</span><br/>
<div>
<template for:each={objQuote.lstComments} for:item="objComment" for:index="index">
<div key={objComment.sRecordId}>
<span if:true={objComment.bEditing}>
<input type="text" value={objComment.sComment}/>
</span>
<span class="comment" if:false={bEditing}>{objComment.sComment}</span>
<span class="createdDate">{objComment.sCreatedDate}</span>
<span class="createdBy">{objComment.sCreatedBy}</span>
<span if:true={objComment.bShowEdit}>
<a onclick={handleEdit} data-pindex={pindex} data-index={index}>Edit</a>
</span>
</div>
</template>
</div>
<div>
<input type="button" name={objQuote.sQuoteId} value="Add Comment" onclick={showCommentBox} />
<input class="hide" data-id={objQuote.sQuoteId} data-index={index} value={objQuote.sComment} onkeyup={changeComment}/>
<input type="button" class="hide" data-index={index} data-id={objQuote.sQuoteId} value="Save" onclick={saveComment} />
</div>
</div>
</div>
</div>
</template>
The error is thrown when I click on Edit anchor tag, resulting in handleEdit js method being called. If you look at the html, you'll understand that I am displaying the comment for respective quote dynamically inside a span using {objComment.sComment} and on click of edit, I'll be displaying the same comment value inside an input field to allow edits to the comment. I use a boolean variable bEditing to hide and show input/span comment.
Below is the JS for better understanding:
#track lstQuotes =[];
#track lstQuotesTemp = [];
#track lstComments = [];
//sComment = '';
#wire(QUOTE_DATA)
quotesData({error, data}){
if(data){
console.log('data : ' ,data);
this.lstQuotes = data;
//Create copy of records
this.lstQuotesTemp = this.lstQuotes.map( obj => ({
...obj
}));
}
else if(error){
console.log('error : ',error);
}
}
The above wire method gets data from back-end which is displayed inside the web component.
Now lstQuotesTemp holds a list of records, and under each record there is a list of comments, lstComments.
I created lstQuotesTemp simply because lstQuotes is read only and changes to it's records would result in error.
Now, let's see what handleEdit method does:
handleEdit(event){
let parentIndex = event.target.dataset.pindex;
let index = event.target.dataset.index;
console.log('Comment Record : ' ,this.lstQuotesTemp[parentIndex].lstComments[index].bEditing);
this.lstQuotesTemp[parentIndex].lstComments[index].bEditing = true;
}
It simply finds the comment record using index to make it editable on click of Edit. However, it seems that the lstComments is still read only even after creation of a copy of it's parent list.
Can someone please suggest a way to fix this error?
I was able to solve the above. The issue was that lstComments under lstQuotesTemp was read-only and thus had to create their copy. Below is what I did :
for(let i=0;i<this.lstQuotesTemp.length; i++){
this.lstQuotesTemp[i].lstComments = this.lstQuotesTemp[i].lstComments.map( obj => ({
...obj
}));
}
It worked as expected after creating a copy of lstQuotesTemp.lstComment.

How to define a temporary variable in Vue.js template

Here is my current template:
<a-droppable v-for="n in curSize" :key="n - 1" :style="{width: `${99.99 / rowLenMap[orderList[n - 1]]}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLenMap[orderList[n - 1]] > 10}">
<some-inner-element>{{rowLenMap[orderList[n - 1]]}}</some-inner-element>
</a-draggable>
</a-droppable>
The problem is that i have to write rowLenMap[orderList[n - 1]] multiple times, and i'm afraid vue.js engine will also calculate it multiple times.
What i want is something like this:
<a-droppable v-for="n in curSize" :key="n - 1" v-define="rowLenMap[orderList[n - 1]] as rowLen" :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
I think it's not difficult to implement technically because it can be clumsily solved by using something like v-for="rowLen in [rowLenMap[orderList[n - 1]]]". So is there any concise and official solution?
I found a very simple (almost magical) way to achieve that,
All it does is define an inline (local) variable with the value you want to use multiple times:
<li v-for="id in users" :key="id" :set="user = getUser(id)">
<img :src="user.avatar" />
{{ user.name }}
{{ user.homepage }}
</li>
Note : set is not a special prop in Vuejs, it's just used as a placeholder for our variable definition.
Source: https://dev.to/pbastowski/comment/7fc9
CodePen: https://codepen.io/mmghv/pen/dBqGjM
Update : Based on comments from #vir us
This doesn't work with events, for example #click="showUser(user)" will not pass the correct user, rather it will always be the last evaluated user, that's because the user temp variable will get re-used and replaced on every circle of the loop.
So this solution is only perfect for template rendering because if component needs re-render, it will re-evaluate the variable again.
But if you really need to use it with events (although not advisable), you need to define an outer array to hold multiple variables at the same time :
<ul :set="tmpUsers = []">
<li v-for="(id, i) in users" :key="id" :set="tmpUsers[i] = getUser(id)" #click="showUser(tmpUsers[i])">
<img :src="tmpUsers[i].avatar" />
{{ tmpUsers[i].name }}
{{ tmpUsers[i].homepage }}
</li>
</ul>
https://codepen.io/mmghv/pen/zYvbPKv
credits : #vir us
Although it doesn't make sense here to basically duplicate the users array, this could be handy in other situations where you need to call expensive functions to get the data, but I would argue you're better off using computed property to build the array then.
Judging by your template, you're probably best off with a computed property, as suggested in the accepted answer.
However, since the question title is a bit broader (and comes up pretty high on Google for "variables in Vue templates"), I'll try to provide a more generic answer.
Especially if you don't need every item of an array transformed, a computed property can be kind of a waste. A child component may also be overkill, in particular if it's really small (which would make it 20% template, 20% logic and 60% props definition boilerplate).
A pretty straightforward approach I like to use is a small helper component (let's call it <Pass>):
const Pass = {
render() {
return this.$scopedSlots.default(this.$attrs)
}
}
Now we can write your component like this:
<Pass v-for="n in curSize" :key="n - 1" :rowLen="rowLenMap[orderList[n - 1]]" v-slot="{ rowLen }">
<a-droppable :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
</Pass>
<Pass> works by creating a scoped slot. Read more about scoped slots on the Vue.js documentation or about the approach above in the dev.to article I wrote on the topic.
Appendix: Vue 3
Vue 3 has a slightly different approach to slots. First, the <Pass> component source code needs to be adjusted like this:
const Pass = {
render() {
return this.$slots.default(this.$attrs)
}
}
Today I needed this and used <template> tag and v-for like this
I took this code and
<ul>
<li v-for="key in keys"
v-if="complexComputation(key) && complexComputation(key).isAuthorized">
{{complexComputation(key).name}}
</li>
</ul>
Changed it to this
<ul>
<template v-for="key in keys">
<li v-for="complexObject in [complexComputation(key)]"
v-if="complexObject && complexObject.isAuthorized">
{{complexObject.name}}
</li>
</template>
</ul>
And it worked and I was pleasantly surprised because I didn't know this was possible
This seems like the perfect use case of a child component. You can simply pass your complex computed value(s) as a property to the component.
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
How about this:
<div id="app">
<div
v-for="( id, index, user=getUser(id) ) in users"
:key="id"
>
{{ user.name }}, {{ user.age }} years old
<span #click="show(user)">| Click to Show {{user.name}} |</span>
</div>
</div>
CodePen: https://codepen.io/Vladimir-Miloevi/pen/xxJZKKx
<template>
<div>
<div v-for="item in itemsList" :key="item.id">
{{ item.name }}
<input v-model="item.description" type="text" />
<button type="button" #click="exampleClick(item.id, item.description)">
Click
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
id: 1,
name: 'Name1',
},
{
id: 2,
name: 'Name2',
},
],
}
},
computed: {
itemsList() {
return this.items.map((item) => {
return Object.assign(item, { description: '' })
})
},
},
methods: {
exampleClick(id, description) {
alert(JSON.stringify({ id, description }))
},
},
}
</script>
Just tested using vue3 and works, i think it works universally
{{ (somevariable = 'asdf', null) }}
<span v-if="somevariable=='asdf'">Yey</span>
<span v-else>Ney</span>
It outputs nothing while setting your variable.
mandatory:
opening "("
set your variable
closing ", null)"
curSize is an array. Your temporary values comprise a corresponding implied array sizedOrderList = curSize.map(n => orderList[n-1]). If you define that as a computed, your HTML becomes
<a-droppable v-for="n, index in sizedOrderList" :key="curSize[index]" :style="{width: `${99.99 / rowLenMap[n]}%`, order: n}">
<a-draggable :class="{thin: rowLenMap[n] > 10}">
<some-inner-element>{{rowLenMap[n]}}</some-inner-element>
</a-draggable>
</a-droppable>

Categories