How to pass parameter to text output in Vue.js 2.x - vue.js

I have this file containing translations to be used in my game application:
{
"de": {
"gamestate": {
"won": "Gewonnen!",
"lost": "Leider verloren!",
"retry": "Nochmal versuchen"
},
"settings": {
"label": {
"playfieldWidth": "Spielfeldbreite",
"playfieldHeight": "Spielfeldhöhe",
"fieldSize": "Feldbreite",
"bombcount": "Anzahl der Bomben"
}
}
},
"en": {
"gamestate": {
"won": "You won!",
"lost": "Game over!",
"retry": "Retry"
},
"settings": {
"label": {
"playfieldWidth": "playfield width",
"playfieldHeight": "playfield height",
"fieldSize": "field size",
"bombcount": "Number of bombs"
}
}
}
}
I put these in a const messages = require('/i18n/translations.json') and attach it to my data property.
In my VM, I have a data property that contains the iso code for the language:
data () {
return {
language: 'en',
messages
}
}
Now when I want to output messages from this, I'd like to go
<p>{{ message('gamestate.won') }}</p>
For this purpose I wrote a computed:
computed: {
message (key) {
let message = messages[this.language]
key.split('.').forEach(function (keypart) {
message = message.hasOwnProperty(keypart) ? message[keypart] : undefined
})
return message || key
}
}
But unfortunately computed properties don't seem to take arguments, and that's why I get
[Vue warn]: Error in render: "TypeError: key.split is not a function"
Next I tried to implement as a methods property instead of a computed, which changes the error to
[Vue warn]: Error in render: "TypeError: _vm.message is not a function"
How can I achieve my goal using Vue.js 2x?

As someone proved via a codepen, my code works. The problem was that before I implemented the translation stuff, I was already using a property message which my endGame() method was trying to fill:
endGame () {
this.winLoseSymbol = '😡'
this.message = 'GAME OVER!' // this was the cause of the problem
this.gamestate = 'lost'
this.stopTimer()
}
Since in the new version message is supposed to be a function, overwriting it with a string causes those parts of the application that were using the function to fail.

Related

Vue.js getting just some attributes from an array to another array

Im trying to PUT some of atributes from array Zamestnanci to array archivovany, I tried almost everything and it seems to not work at all. I allways get HTTP error 415 or HTTP 400 because the atributes in archivovany are null.
<script>
import axios from 'axios';
import ZamestnanciService from "../ZamestnanciService";
export default {
name: 'Zamestnanci',
data() {
return {
zamestnanci: [{
id:"",
meno:"",
priezvisko:"",
pozicia:"",
}],
archivovany: [{
id: "",
meno: "",
priezvisko: "",
datumPrepustenia: new Date(),
poslednaPozicia: "",
}]
,
};
},
created(){
this.getZamestnanci();
},
methods: {
getZamestnanci(){
axios.get('https://localhost:49153/api/zamestnanci').then(response => {
this.zamestnanci = response.data;
console.log(response.data);})
}, //get
archivovat(id){
axios.post('https://localhost:49153/api/archivovany/',this.archivovany).then(res=>{
console.log(res);})
},
deleteZamestnanci(id){
axios.delete('https://localhost:49153/api/zamestnanci/'+id).then(response => {this.getZamestnanci();})
if(confirm("chcete archivovať zamestnanca ?")){
this.archivovat();
}
},//delete
}
}
</script>
I need to pass id, meno, priezvisko, pozicia to array archivovany and then PUT the data to another table, but nothing seems to work can anyone please help me ?
The data is from asp.net core API
Okay so I solved it my way, I don't know if its the best way but here is my solution.
i created one more object zamestnanec.
export default {
name: 'Zamestnanci',
data() {
return {
zamestnanci:"",
zamestnanec:"",
archivovany: {
meno: "",
priezvisko: "",
adresa:"",
datumNarodenia:"",
datumPrepustenia: new Date(),
pozicia: "",
plat: ""
}
I created one more axios.get function to load data to the object inside the function Archivovat(id) and there i saved the data from get function to zamestnanec and after that I have put the data to object where i needed.
archivovat(id){
axios.get('https://localhost:49153/api/zamestnanci/'+id).then(response => {
this.zamestnanec = response.data;
this.archivovany.meno=this.zamestnanec.meno;
this.archivovany.priezvisko=this.zamestnanec.priezvisko;
this.archivovany.adresa=this.zamestnanec.adresa;
this.archivovany.datumNarodenia=this.zamestnanec.datumNarodenia;
this.archivovany.pozicia=this.zamestnanec.pozicia;
this.archivovany.plat=this.zamestnanec.plat;
console.log(response.data);
axios.post('https://localhost:49153/api/archivovany/',this.archivovany).then(res=>{
console.log(res);})})
},
I'ts really messy code but it solved my problem.

Vue Bootsrap component does not render after Vuex Store Update. using watch and nextTick

In a vue cli app, I can't figure out how to make an object in a component re-render when there is a change in the Vuex store made by another component. In this example, I change the curDocTypevariable, but the b-form-select object remains the same value (initially 0). Not sure if the problem is in the Bootsrap side or in Vue.
I have this in one component1.vue :
HTML
<b-form-select v-model="curDocType" :options="options" class="mb-3" v-bind:style="{'background-color': activeColor}">
SCRIPT
data() {
return {
options: [
{ value: 1, text: 'Sale' },
{ value: 5, text: 'CrMemo' },
{ value: 0, text: 'Quote' },
{ value: 4, text: 'CashIn' },
],
curDocType : this.$store.state.curDocType,
activeColor : '#9EDE7D' //Red
}
},
watch:{
curDocType(newval,oldval){
if (newval == 5) this.activeColor = '#D67373'
else this.activeColor = '#9EDE7D'
if (this.$store.state.curOrder == ""){
this.$store.commit('setcurDocType', newval)
}else{
this.$nextTick(() => {this.curDocType=this.$store.state.curDocType})
}
}
}
Now, I have another component2.vue, where I call a method that changes the curDocType in the Vuex Store:
convertOrder(){
if (this.$store.state.curOrder != ""){
axios.post(this.ApiBaseUrl + 'PosFunctions/QuoteToOrder',
JSON.stringify(this.$store.state.curOrder),
{headers: {"Content-Type": "application/json"}})
.then((response) => {
var res = response;
this.$store.commit('setcurDocType', 1) //!!IMPORTANT
this.RecoverSale(response.data.return_value)
})
}
}
Store.js:
setcurDocType (state, DocType) {
state.curDocType = DocType
},
Versions:
bootstrap-vue#2.16.0
bootstrap#4.5.2"
vue#2.6.12
The problem must come from data(), i don't think calling $store from it will works. (even calling "this" in it is an error but i'm not 100% sure)
Moreover, you shouldn't set a store state to a v-model, since a store state is a "getter" only and a v-model is a two-way binding (getter + setter)
You could do a computed :
curDocType : {
get(){
return this.$store.state.curDocType
}
set(value){
this.$store.commit('setcurDocType', 1);
}
}
With this, binding curDocType to the v-model is correct.
Hope this will fix your problem

How to create getters and setters for all sub-properties of a Vuex state property efficiently?

I couldn't find the answer anywhere.
Let's say we have Vuex store with the following data:
Vuex store
state: {
dialogs: {
dialogName1: {
value: false,
data: {
fileName: '',
isValid: false,
error: '',
... 10 more properties
}
},
dialogName2: {
value: false,
data: {
type: '',
isValid: false,
error: '',
... 10 more properties
}
}
}
}
Dialogs.vue
<div v-if="dialogName1Value">
<input
v-model="dialogName1DataFileName"
:error="dialogName1DataIsValid"
:error-text="dialogName1DataError"
>
<v-btn #click="dialogName1Value = false">
close dialog
</v-btn>
</div>
<!-- the other dialogs here -->
Question
Let's say we need to modify some of these properties in Dialogs.vue.
What's the best practices for creating a getter and setter for every dialog property efficiently, without having to do it all manually like this:
computed: {
dialogName1Value: {
get () {
return this.$store.state.dialogs.dialogName1.value
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.value', value: value })
}
},
dialogName1DataFileName: {
get () {
return this.$store.state.dialogs.dialogName1.data.fileName
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.fileName', value: value })
}
},
dialogName1DataIsValid: {
get () {
return this.$store.state.dialogs.dialogName1.data.isValid
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.isValid', value: value })
}
},
dialogName1DataIsError: {
get () {
return this.$store.state.dialogs.dialogName1.data.error
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.error', value: value })
}
},
... 10 more properties
And this is only 4 properties...
I suppose I could generate those computed properties programmatically in created(), but is that really the proper way to do it?
Are there obvious, commonly known solutions for this issue that I'm not aware of?
getters can be made to take a parameter as an argument - this can be the 'part' of the underlying state you want to return. This is known as Method-style access. For example:
getFilename: (state) => (dialogName) => {
return state.dialogs[dialogName].data.fileName
}
You can then call this getter as:
store.getters.getFilename('dialogName1')
Note that method style access doesn't provide the 'computed property' style caching that you get with property-style access.
For setting those things in only one central function you can use something like this:
<input
:value="dialogName1DataFileName"
#input="update_inputs($event, 'fileName')">
// ...
methods:{
update_inputs($event, whichProperty){
this.$store.commit("SET_PROPERTIES", {newVal: $event.target.value, which:"whichProperty"})
}
}
mutation handler:
// ..
mutations:{
SET_PROPERTIES(state, payload){
state.dialogName1.data[payload.which] = payload.newVal
}
}
Let me explain more what we done above. First we change to v-model type to :value and #input base. Basically you can think, :value is getter and #input is setter for that property. Then we didn't commit in first place, we calling update_inputs function to commit because we should determine which inner property we will commit, so then we did send this data as a method parameter (for example above code is 'fileName') then, we commit this changes with new value of data and info for which property will change. You can make this logic into your whole code blocks and it will solved your problem.
And one more, if you want to learn more about this article will help you more:
https://pekcan.dev/v-model-using-vuex/

How in store object to get access to parent vue page?

In my vue 2.6/cli 4/vuex 3.1 app I update some data in store vuex, like in src/store/index.js :
userPropStore(context, userProp ) {
bus.$emit( 'beforeUserPropStore', userProp );
let apiUrl = process.env.VUE_APP_API_URL
Vue.http.post(apiUrl + '/personal/user-props', userProp).then(({data}) => {
let userProps= this.getters.userProps
userProps.push({
"id": data.user_props.id,
"name": data.user_props.name,
"value": data.user_props.value,
"created_at": data.user_props.created_at,
})
this.commit('refreshUserProps', userProps);
bus.$emit( 'onUserPropStoreSuccess', data );
}, error => {
console.log('context::')
console.log(context)
console.error(error)
self.$refs.userObserverForm.setErrors(error.body.errors)
});
}, // userPropStore(context, userProp ) {
and using ValidationProvider of vee-validate 3.2 I want to catch server errors (like not unique item)
but I got error :
index.js?4360:847 Uncaught (in promise) TypeError: Cannot read property 'userObserverForm' of undefined
on line
self.$refs.userObserverForm.setErrors(error.body.errors)
If there is a way in store object to get access to parent page, maybe with context, which has : https://imgur.com/a/P4St8Ri
?
Thanks!
This is a very strange implementation.
Obviously self.$refs.userObserverForm.setErrors(error.body.errors) fails because you are NOT in a component, which is where $refs is available.
What you have to do in your catch block is setting the errors in Vuex and then make your component read from there.
Pseudocode follows:
I don't understand what your bus is doing... I guess you use it to send data to your component, but why use Vuex then?
userPropStore(context, userProp ) {
bus.$emit( 'beforeUserPropStore', userProp );
let apiUrl = process.env.VUE_APP_API_URL
Vue.http.post(apiUrl + '/personal/user-props', userProp).then(({data}) => {
let userProps= this.getters.userProps
userProps.push({
"id": data.user_props.id,
"name": data.user_props.name,
"value": data.user_props.value,
"created_at": data.user_props.created_at,
})
this.commit('refreshUserProps', userProps);
bus.$emit( 'onUserPropStoreSuccess', data );
commit('SET_USER_PROPS, data)
}, error => {
console.log('context::')
console.log(context)
console.error(error)
commit('SET_USER_PROPS_ERRORS', error.body.errors)
});
}
The way I've done this in the past is to return the promise that Vue.http.post produces and then do what you want in your component with the failure information:
userPropStore(context, userProp ) {
bus.$emit( 'beforeUserPropStore', userProp );
let apiUrl = process.env.VUE_APP_API_URL
//add return to the line below
return Vue.http.post(apiUrl + '/personal/user-props', userProp).then(({data}) => {
Then over in your component:
loadData() {
this.$store.dispatch('userPropStore').catch((error) => {
this.$refs.userObserverForm.setErrors(error.body.errors)
//any other things you want to do "in component" goes here
});
}

How to generate Items list with vue-paypal-checkout?

I am trying to generate an items list response from paypal checkout requests. I am trying to do it dynamically, using my data objects and some computed properties in a for in loop. As far as I have understood, my items_list will always need to be a data variable, never a hard-coded array.
Here is my template element:
<div v-bind:key="plan.key" v-for="plan in plans" >
<PayPal
:amount="plan.price" // all good
currency="GBP" // all good
:client="credentials" // all good
env="sandbox" // all good
:items="[plan]" // this is NOT working
#payment-authorized="payment_authorized_cb" // all good
#payment-completed="payment_completed_cb" // all good
#payment-cancelled="payment_cancelled_cb" // all good
>
</PayPal>
</div>
Here are my data objects on my script:
plans: {
smallPlan: {
name: 'Small Venue',
price: '6',
},
mediumPlan: {
name: 'Medium Department',
price: '22',
},
}
payment_completed: {
payment_completed_cb() {
}
},
payment_authorized: {
payment_authorized_cb() {
}
},
payment_cancelled: {
payment_cancelled_cb() {
}
},
Here are my methods:
methods: {
payment_completed_cb(res, planName){
toastr.success("Thank you! We'll send you a confirmation email soon with your invoice. ");
console.log(res);
},
payment_authorized_cb(res){
console.log(res);
},
payment_cancelled_cb(res){
toastr.error("The payment process has been canceled. No money was taken from your account.");
console.log(res);
},
The documentation of Vue-paypal-checkout is available here https://www.npmjs.com/package/vue-paypal-checkout
If I don't add the items list :items everything works perfectly:
{"id":"PAY-02N9173803167370DLPMKKZY","intent":"sale","state":"approved","cart":"90B34422XX075534E","create_time":"2018-10-30T18:39:51Z","payer":{"payment_method":"paypal","status":"VERIFIED","payer_info":{"email":"joaoalvesmarrucho-buyer#gmail.com","first_name":"test","middle_name":"test","last_name":"buyer","payer_id":"JCZUFUEQV33WU","country_code":"US","shipping_address":{"recipient_name":"test buyer","line1":"1 Main St","city":"San Jose","state":"CA","postal_code":"95131","country_code":"US"}}},"transactions":[{"amount":{"total":"245.00","currency":"GBP","details":{}},"item_list":{},"related_resources":[{"sale":{"id":"2RA79134UX2301839","state":"pending","payment_mode":"INSTANT_TRANSFER","protection_eligibility":"ELIGIBLE","parent_payment":"PAY-02N9173803167370DLPMKKZY","create_time":"2018-10-30T18:39:50Z","update_time":"2018-10-30T18:39:50Z","reason_code":"RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION","amount":{"total":"245.00","currency":"GBP","details":{"subtotal":"245.00"}}}}]}]}
But if I add :items="[plan]" i get this error message:
Uncaught Error: Error: Request to post https://www.sandbox.paypal.com/v1/payments/payment failed with 400 error. Correlation id: 19238526650f5, 19238526650f5
{
"name": "VALIDATION_ERROR",
"details": [
{
"field": "transactions.item_list.items.item_key",
"issue": "This field name is not defined for this resource type"
}
],
"message": "Invalid request - see details",
"information_link": "https://developer.paypal.com/docs/api/payments/#errors",
"debug_id": "19238526650f5"
Any thoughts?
Also if you happen to know, is there a way to sell/implement a subscription instead of a one-off transaction using Vue-paypal-checkout?
Many thanks