link2192 link2193 link2194 link2195 link2196 link2197 link2198 link2199 link2200 link2201 link2202 link2203 link2204 link2205 link2206 link2207 link2208 link2209 link2210 link2211 link2212 link2213 link2214 link2215 link2216 link2217 link2218 link2219 link2220 link2221 link2222 link2223 link2224 link2225 link2226 link2227 link2228 link2229 link2230 link2231 link2232 link2233 link2234 link2235 link2236 link2237 link2238 link2239 link2240 link2241 link2242 link2243 link2244 link2245 link2246 link2247 link2248 link2249 link2250 link2251 link2252 link2253 link2254 link2255 link2256 link2257 link2258 link2259 link2260 link2261 link2262 link2263 link2264 link2265 link2266 link2267 link2268 link2269 link2270 link2271 link2272 link2273 link2274 link2275 link2276 link2277 link2278 link2279 link2280 link2281 link2282 link2283 link2284 link2285 link2286 link2287 link2288 link2289 link2290 link2291 link2292 link2293 link2294 link2295 link2296 link2297 link2298 link2299 link2300 link2301 link2302 link2303 link2304 link2305 link2306 link2307 link2308 link2309 link2310 link2311 link2312 link2313 link2314 link2315 link2316 link2317 link2318 link2319 link2320 link2321 link2322 link2323 link2324 link2325 link2326 link2327 link2328

[Vue.js] Vue Multiselect is not submitting ID value (Rails API)

My app is a Rails API backend and VueJS via Nuxt front end.

there is a form where one of the inputs is a select and I’m using vue-multiselect. The select options are values from a different table, where to display the name field, but submit the ID.

when able to display the options in the drop drown ok, and I’m also submitting other values in the form, but the ID is not working.

Rails console shows the error of distillery_id not being a permitted parameter, although I do have this set in the controller.

Started POST “/api/v1/gins” for ::1 at 2019-02-01 13:25:38 +0000
Processing by Api::V1::GinsController#create as HTML
Parameters: {“gin_name”=>”distillery_id”, “description”=>”distillery_id should be submitted”, “distillery_id”=>{“id”=>3, “distillery_name”=>”Gordon’s”, “snippet”=>nil, “description”=>nil, “website”=>nil, “country”=>”United Kingdom”, “created_at”=>”2019-01-29T13:46:15.088Z”, “updated_at”=>”2019-01-29T13:46:15.088Z”, “slug”=>nil}, “abv”=>”0”, “snippet”=>”distillery_id now?”, “gin”=>{“gin_name”=>”distillery_id”, “snippet”=>”distillery_id now?”, “description”=>”distillery_id should be submitted”, “abv”=>”0”, “distillery_id”=>{“id”=>3, “distillery_name”=>”Gordon’s”, “snippet”=>nil, “description”=>nil, “website”=>nil, “country”=>”United Kingdom”, “created_at”=>”2019-01-29T13:46:15.088Z”, “updated_at”=>”2019-01-29T13:46:15.088Z”, “slug”=>nil}}
Unpermitted parameter: :distillery_id

gins_controller.rb


def gin_params
params.require(:gin).permit(:gin_name, :alcoholic, :snippet, :description, :abv, :distillery_id)
end

new.vue

<template>
<section class=”container”>
<div>
<h1>Gins</h1>
<form @submit.stop.prevent=”addGin”>
<h2>New Gin</h2>
<p>
<label for=”gin_name” class=”input-label”>Title:</label>
<input id=”gin_name” v-model=”gin_name” type=”gin_name” name=”gin_name” class=”input”>
</p>
<p>
<label for=”snippet” class=”input-label”>Snippet:</label>
<input id=”snippet” v-model=”snippet” type=”text” name=”snippet” class=”input”>
</p>
<p>
<label for=”description” class=”input-label”>Description:</label>
<input id=”description” v-model=”description” type=”textarea” name=”description” class=”input”>
</p>
<p>
<label for=”abv” class=”input-label”>ABV%:</label>
<input id=”abv” v-model=”abv” type=”number” name=”abv” class=”input”>
</p>
<div>
<label for=”distillery_id” class=”input-label”>Distillery:</label>
<multiselect
v-model=”distillery_id”
track_by=”distillery_id”
:options=”options”
:searchable=”true”
placeholder=”Choose One Distillery”
:custom-label=”label”
\>
</multiselect>
</div>
<p>
<input type=”submit” value=”Submit” class=”button”>
</p>
</form>
</div>
</section>
</template>
<script>
import axios from ‘axios’
import Multiselect from ‘vue-multiselect’

export default {

components: { Multiselect },
data() {
return {
gin_name: ‘’,
snippet: ‘’,
description: ‘’,
abv: ‘’,
distillery_id: ‘’,
options: []
}
},

mounted() {
this.getDistilleries()
},

methods: {

label(option) {
return `${option.distillery_name}`
},

addGin() {
axios.post(‘http://localhost:4000/api/v1/gins', {
gin_name: this.gin_name, description: this.description, distillery_id: this.distillery_id, abv: this.abv, snippet: this.snippet
})
.then((response) => {})
console.log()
},
getDistilleries(req) {
axios.get(‘/api/v1/distilleries’)
.then((res) => {
this.options = res.data
})
.catch((error) => {
console.log(error)
})
}

}
}
</script>
<style src=”vue-multiselect/dist/vue-multiselect.min.css”></style>
<style>

</style>

Based on the console, I suspect this is a rails issue rather than vue, but that permitted params looks good to me.

Any suggestions what else could be amiss?

Solution :

Have you tried to use a filter to get the good values?

addGin() {
let myId = options.filter(o => distillery_id.some(d => d.distillery_id === o.distillery_id));
axios.post(‘http://localhost:4000/api/v1/gins', {
gin_name: this.gin_name,
description: this.description,
distillery_id: myId,
abv: this.abv,
snippet: this.snippet
}).then(console.log)
},

I’m not sut that this works, check the code before use.

Other way is debug the code in the line and check how you could get the id:

addGin(argument1, argument2, argument3) {
debugger; // The code will stop here, check this object to get the data and change the code!
axios.post(‘http://localhost:4000/api/v1/gins', {
gin_name: this.gin_name,
description: this.description,
distillery_id: this.distillery_id,
abv: this.abv,
snippet: this.snippet
}).then(console.log)
},

I suggest to change multiselect to vue-select
The docs seems more readable.

Hope it helps :)

[Vue.js] Vue.JS Quiz with score - 2 anwsers work, 3 doens't

there is a Quiz / survey with a score written in Vue.js.

In this example there is only 1 correct answer, at the end, the score is shown and the redirect takes you to a different page (not included in this demo).

When the question has 3 answers you cannot select the first radio button, it jumps straight to the second one.

What causes this behaviour?

https://codepen.io/bucky208/pen/LMQvxr

// Create a quiz object with a title and questions.
// A question has one or more answer, and one or more is valid.
var quiz =
{
title: ‘Reachers’,
questions: [
{
text: “Wat stuurt het verkoopgesprek het meest?”,
responses: [
{text: “De vraag”},
{text: “Het antwoord”, correct: true},
]
},
{
text: “Welke argumenten overtuigen het meest ?”,
responses: [
{text: “Rationele”},
{text: “Emotionele”, correct: true},
]
},
{
text: “In een sector met hevige concurrentie tussen evenwaardige leveranciers. Wat zal uiteindelijk het meest de doorslag geven bij de uiteindelijke keuze van de klant?”,
responses: [
{text: “Logistie”},
{text: “Contact”},
{text: “Prijs”, correct: true},
]
},
{
text: “In hoeveel percent van de gevallen is het bezwaar U bent te duur ook effectief de oorzaak waarom men niet bij u koopt ?”,
responses: [
{text: “90%”},
{text: “50%”},
{text: “10%”, correct: true},
]
},
{
text: “Waar hebt u als verkoper het meeste aan ?”,
responses: [
{text: “U krijgt direct bij aankomst een koffie”},
{text: “Tijdens het gesprek wordt u na enige tijd een koffie aangeboden”, correct: true},
]
},
{
text: “Wat is voor de klant het belangrijkste criterium om u te aanzien als volwaardige gesprekspartner ?”,
responses: [
{text: “Uw vlotheid en charme”},
{text: “Uw vakkennis”},
{text: “Uw voorbereiding”, correct: true},
]
},
{
text: “Welke klanten hebben voorrang voor elk bedrijf ?”,
responses: [
{text: “De bestaande klanten”},
{text: “De potentile klanten”, correct: true},
]
},
{
text: “Hoe kondigt u best prijsverhogingen aan ?”,
responses: [
{text: “U denkt mee met de klant en onderhandelt”},
{text: “U presenteert ze als voldongen feiten”, correct: true},
]
},
{
text: “Bij de aanvang van het verkoopgesprek vertelt de klant dat hij heel tevreden is van huidige leverancier. Wat doet u ?”,
responses: [
{text: “U luistert en gaat er niet op in.”},
{text: “U haalt uw argumenten naar boven waarom klanten voor u en uw bedrijf kiezen”, correct: true},
]
},
{
text: “Tijdens het verkoopgesprek vertelt de klant dat hij een probleem heeft bij zijn huidige leverancier. Wat doet u ?”,
responses: [
{text: “U haalt uw eigen argumenten naar boven”},
{text: “U doet niets”},
{text: “U diept het probleem verder uit.”, correct: true},
]
},
{
text: “Na verloop van tijd wordt u bevriend met de klant. Deze ontwikkeling is in uw:”,
responses: [
{text: “Voordeel”},
{text: “Nadeel”, correct: true},
]
},
{
text: “Wat is de juiste volgorde waarin u uw presentatie opbouwt ?”,
responses: [
{text: “1. Uzelf verkopen 2. Uw producten verkopen 3. Uw bedrijf verkopen”},
{text: “1. Uw producten verkopen 2. Uw bedrijf verkopen 3. Uzelf verkopen”},
{text: “1. Uzelf verkopen 2. Uw bedrijf verkopen 3. Uw producten verkopen”, correct: true},
]
},
{
text: “Als puntje bij paaltje komt kies ik uiteindelijk voor:”,
responses: [
{text: “Mijn klant”},
{text: “Mijn bedrijf”, correct: true},
]
},
{
text: “Het beste wapen tegen prijsdruk is:”,
responses: [
{text: “Scherpe prijzen”},
{text: “De relatie met de klant”},
{text: “Argumenten”, correct: true},
]
},
{
text: “Om projecten te verkopen, met meerdere stakeholder binnen het bedrijf, hanteer ik best de volgende bezoekvolgorde:”,
responses: [
{text: “Eerst de eigenaar, dan management en tenslotte uitvoerende personeel”},
{text: “Eerst uitvoerend personeel, daarna management en tot slot de eigenaar”},
{text: “Eerst management, daarna de eigenaar en tot slot uitvoerend personeel”, correct: true},
]
},
]
};

/*
var quiz =
{
title: ‘Reachers’,
questions: [
{
text: “Wat stuurt het verkoopgesprek het meest?”,
responses: [
{text: “De vraag”},
{text: “Het antwoord”, correct: true},
]
}
]
};
*/

new Vue({
el: ‘#app’,
data: {
quiz: quiz,
// Store current question index
questionIndex: 0,
show: false,
// An array initialized with “false” values for each question
// It means: “did the user answered correctly to the question n?” “no”.
userResponses: Array(quiz.questions.length).fill(false),
},
// The view will trigger these methods on click
methods: {
checkbutton: function() {
this.show = true;
},
// Go to next question
next: function() {
this.questionIndex++;
this.show = false;
console.log(“next”);
if(this.questionIndex === this.quiz.questions.length) {
if(this.score() <= 5) {
window.location = “/score-0-tot-5/“;
console.log(“5 of minder”);
} else if(this.score() > 5 && this.score() <= 10) {
window.location = “/score-5-tot-10/“;
onsole.log(“tussen 5 en 10”);
} else if(this.score() > 10) {
window.location = “/score-10-tot-15/“;
onsole.log(“10 of meer”);
}
}
},
// Go to previous question
prev: function() {
this.questionIndex–;
},
// Return “true” count in userResponses
score: function() {
return this.userResponses.filter(function(val) { return val }).length;
}
}
});
<div id=”app”>
<!– index is used to check with current question index –>
<div v-for=”(question, index) in quiz.questions”>
<!– Hide all questions, show only the one with index === to current question index –>
<div v-show=”index === questionIndex”>
<h4>{index+1}. { question.text }</h4>
<ol>
<li v-for=”response in question.responses”>
<label>
<!– The radio button has three new directives –>
<!– v-bind:value sets “value” to “true” if the response is correct –>
<!– v-bind:name sets “name” to question index to group answers by question –>
<!– v-model creates binding with userResponses –>
<input type=”radio”
v-bind:value=”response.correct”
v-bind:name=”index”
v-model=”userResponses[index]“ v-on:click=”checkbutton”> {response.text}
</label>
</li>
</ol>
<!– The two navigation buttons –>
<!– Note: prev is hidden on first question –>
<button v-if=”questionIndex > 0” v-on:click=”prev”>
Vorige
</button>
<button v-on:click=”next” v-show=”show”>
Volgende
</button>
<button v-on:click=”next” v-show=”!show” disabled>
Volgende
</button>
</div>
</div>
<div v-show=”questionIndex === quiz.questions.length”>
<h2>
Bedankt voor uw tijd
</h2>
<p>
Uw score is: { score() } / { quiz.questions.length }, we sturen U nu door naar een andere pagina…
</p>

</div>
</div>
<script src=”https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Kind regards,

Maxim

Solution :

Because with 3 options you have 2 items with the same value, as you can see in this image:

Every item in responses array should have a different value, so I can suggest you to add another property for it, and use the correct property only to check the right answer.

Solution 2:

thanks, fixed it with v-bind:value=”response.text”

and

score: function() {
correctCount = 0;
that = this;
this.quiz.questions.filter(function(value, i) {
value.userAnswerCorrect = false;
value.userAnswer = that.userResponses[i];
value.responses.filter(function(answer, j) {
if (answer.correct == true && value.userAnswer == answer.text) {
correctCount++;
}
})
});
return correctCount;
}

[Vue.js] VanillaJS to VueJS Error bad element for Flickity carousel

I’m trying to get this Flickity example (CodePen) to work in a VueJS component.

HTML

<div class=”carousel”>
<div class=”carousel-cell”></div>
<div class=”carousel-cell”></div>
<div class=”carousel-cell”></div>
<div class=”carousel-cell”></div>
<div class=”carousel-cell”></div>
</div>

JS

var flkty = new Flickity(‘.carousel’);

flkty.on( ‘dragStart’, function() {
console.log(‘dragStart’);
});

I get this error:

bad element for Flickity: carousel

My not working version:

JS data ()

import Flickity from ‘flickity’
var flkty = new Flickity(‘.carousel’)
export default {
data () {
return {
flickityOptions: {
dragThreshold: 50,
initialIndex: 1,
prevNextButtons: false,
pageDots: false,
wrapAround: false,
hash: true,
percentPosition: false
},

JS mounted ()

mounted () {
flkty.on(‘dragStart’, function () {
this.stageDragging = true
console.log(‘stageDragging: ‘ + this.stageDragging)
})
flkty.on(‘dragEnd’, function () {
this.stageDragging = false
console.log(‘stageDragging: ‘ + this.stageDragging)
})

How to use this Flickity example in a VueJS component?

Solution :

In the <script> section, the JavaScript executes before the template has been rendered in the document, so Flickity wouldn’t be able to find .carousel if called outside a hook. vue.js instances have a mounted() lifecycle hook that gets called when its template has rendered, which is where you should create the Flickity carousel:

// const flkty = new Flickity(…) // DON’T DO THIS HERE
export default {
mounted() {
this.$nextTick(() => {
const flkty = new Flickity(…)
// …
});
}
}

Also, instead of passing the .carousel classname to Flickity, pass a ref to the .carousel element in order to avoid grabbing an unexpected .carousel element in the document (e.g., if you had multiple component instances).

// template
<div ref=”carousel”>

// script
new Flickity(this.$refs.carousel)

demo

Solution 2:

var app = new Vue({
el: ‘#app’,
data() {
return {
options: {
dragThreshold: 50,
initialIndex: 1,
prevNextButtons: false,
pageDots: false,
wrapAround: false,
hash: true,
percentPosition: false,
},
itemsCount: 5,
flkty: null,
}
},
computed: {
items() {
return […Array(this.itemsCount)].map((_,i) => i + 1);
}
},
mounted: function () {
this.flkty = new Flickity(`#${this.$el.id} .carousel`);
this.flkty.on( ‘dragStart’, function() {
console.log(‘dragStart’);
});
}
})

Vue.config.devtools = false;
Vue.config.productionTip = false;
* { box-sizing: border-box; }

body { font-family: sans-serif; }

.carousel {
background: #FAFAFA;
}

.carousel-cell {
width: 66%;
height: 200px;
margin-right: 10px;
background: #8C8;
border-radius: 5px;
counter-increment: carousel-cell;
}

.carousel-cell:before {
display: block;
text-align: center;
content: counter(carousel-cell);
line-height: 200px;
font-size: 80px;
color: white;
}
<link href=”https://npmcdn.com/flickity@2/dist/flickity.css" rel=”stylesheet”/>
<script src=”https://npmcdn.com/flickity@2/dist/flickity.pkgd.js"></script>
<script src=”https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id=”app”>
<div class=”carousel”>
<div v-for=”item in items” class=”carousel-cell”>{ item }</div>
</div>
</div>

[Vue.js] How can i render the result of createElement() in Vue.js without creating a component

My goal is to build a test suite to visualize the differences of implementation of the inner hyperscript method createElement() (otherwise known as h()) within React, preact, Inferno, Snabbdom, Vue..

In React, i can invoke it like that (without building a component) :

ReactDOM.render(
React.createElement(‘div’, { className: ‘preview’ }, [‘Hello World’]),
document.getElementById(‘react-preview’)
)

In Preact, we simply do :

preact.render(
preact.h(‘div’, { className: ‘preview’ }, [‘Hello World’]),
document.getElementById(‘preact-preview’)
)

With Inferno.. i must import inferno and inferno-hyperscript :

Inferno.render(
Inferno.h(‘div’, { className: ‘preview’ }, [‘Hello World’]),
document.getElementById(‘inferno-preview’)
)

Now, i’m still trying to figure how to do this in vue.js without creating a component : i don’t want to deal with the additional cost of components instances creation, i just want to visualize and compare the raw virtual dom creation and rendering process of each library.

there is found a way to do it in this post, but it still create a new vue.js instance..

new Vue({
render: h => h(‘div’, { className: ‘preview’ }, [‘Hello World’])
}).$mount(‘#vue-preview’)

Solution :

This is something that is not usually done in the vue.js world, and because the way vue.js “listens” to variable changes, it by default comes with an instance that actually does the listening.

This is the major difference between vue.js and other frameworks, where in other frameworks you need to call the render function manually, vue.js modifies the original objects and watches on them.

If you are only interested in the final DOM structure, just destroy the vue.js object once you are done.

new Vue({
render: h => h(‘div’, { className: ‘preview’ }, [‘Hello World’])
}).$mount(‘#vue-preview’).$destroy()

Solution 2:

You have to use slots for this:

https://vuejs.org/v2/guide/components-slots.html

Solution 3:

Quick way is to access the render method.

var app = new Vue({
el: ‘#app’,
data() {
return {
isRed: true
}
},

/*
* Same as
* <template>
* <div :class=”{‘is-red’: isRed}”>
* <p>Example Text</p>
* </div>
* </template>
*/
render(h) {
return h(‘div’, {
‘class’: {
‘is-red’: this.isRed
}
}, [
h(‘p’, ‘Example Text’)
])
}
})

[Vue.js] Images not loading after Firebase hosting deployed

So, when building this SPA website using mainly Vuejs. Anyway, everything works fine when I build the website through Webpack and its dev server. After deploying it with Firebase hosting, however, the images stop loading.

You can visit the website here, and see for yourself: https://tasker-2d87e.firebaseapp.com/
As you can see, only the favicon loads. The logo, and the hamburger icon do not load with the favicon.

Here’s the firebase config:

{
“functions”: {
“predeploy”: [
“npm –prefix \“$RESOURCE_DIR\“ run lint”
],
“source”: “functions”
},
“hosting”: {
“public”: “dist”,
“ignore”: [
“firebase.json”,
“**/.*“,
“**/node_modules/**“
],
“rewrites”: [
{
“source”: “**“,
“destination”: “/index.html”
}
]
}
}

Additionally, if you go to the “resources” tab of Chrome’s Dev Tool, you can see the 2 images inside the webpack folder.

I just want to know why the images weren’t loading, and if there are any ways to fix it. Thanks in advance.

Solution :

the URLs are looking for a subfolder named /dist/, but the public folder is indeed dist, so you just need to take that out of the image tags.

For example, replace https://tasker-2d87e.firebaseapp.com/dist/tasker\_logo.png with https://tasker-2d87e.firebaseapp.com/tasker\_logo.png and you’ll see the image you’re looking for.

[Vue.js] vuejs how to compile single file component to js object

there is a simple vue.js js single file component:

<template>
<div>
Hello!
</div>
</template>

<script>
export default {
name: ‘app’
}
</script>

And to compile it to

{
template: ‘<div>Hello!</div>’
}

of course, it should work with more complex examples, with included components and such.

How do I do that?

Solution :

You can use vue-template-compiler package:

Install it:

npm i vue-template-compiler

Use it (test.vue.js is a component from the example):

const fs = require(‘fs’);
const compiler = require(‘vue-template-compiler’);

const content = fs.readFileSync(‘./test.vue’, ‘utf-8’);

const res = compiler.parseComponent(content);
const template = res.template.content.trim();

const result = { template };
console.log(result);

Result:

{ template: ‘<div>\n Hello!\n</div>’ }

Don’t forget to add error checks to the code.

Solution 2:

You may want in this case convert the .vue.js file to a .js, then write and export the component as

export default Vue.component({
template: ‘<div>Hello!</div>’
})

and then import it anywhere you want within the vue.js instance.

[Vue.js] How to block modal until api request is resolved

The question

What is the best way I can block the hideModal() action, ESC keydown handler, overlay click handler until the EditDepartmentsModal::submitChanges()’s underlying http request is resolved?

Update

What I think I can do is to:

intercept api calls, set PENDING status at another vuex module
check if PENDING is true in the hideModal() action.

Wouldn’t that couple the vuex modules? Is it a good idea at all?

The details

I’m using a separate vuex module for a modal visibility control (as proposed in the article):


state: { modalVisible: false, },
mutations: {
SET_MODAL_VISIBLE(state) { state.modalVisible = true; },
SET_MODAL_INVISIBLE(state) { state.modalVisible = false; },
},
actions: {
showModal(context) { context.commit(‘SET_MODAL_VISIBLE’); },
hideModal(context) { context.commit(‘SET_MODAL_INVISIBLE’); },
}

The Modal.vue.js (excerpt):

<template>
<div class=”ui-modal”>
<div v-if=”visible” @click=”closeOnOverlayClicked && hideModal()”>
<div :class=”cssContainerClasses” @click.stop>
<slot></slot>
</div>
</div>
</div>
</template>

mounted() {
if(this.closeOnEscPressed) this.handleEscPressed();
},
computed: {
…mapState(‘modal’, { visible: state => state.modalVisible, }),
},
methods: {
// handles ESC keydown, overlay click
…mapActions(‘modal’, [‘hideModal’,]),
// other methods …
}

The parent EditDepartmentsModal.vue.js component embeds the Modal.vue.js and allows firing its submitChanges(), cancel() methods:

<template>
<modal>
<form>
<!– … –>
<div class=”modal-actions”>
<button @click=”submitChanges()”>Submit</button>
<button @click=”cancel()”>Cancel</button>
</div>
</form>
</modal>
</template>

<script>

methods: {
submitChanges() {
// DepartmentsHttpService was imported before
let rq = DepartmentsHttpService.saveChanges();
rq.then(r => { this.hideModal(); });
rq.catch(e => { /* show errors, log */ this.hideModal(); });
},
cancel() {
// vuex mapped action
this.hideModal();
}
}

</script>

For api calls I decided to utilize (article) service objects wrapping the axios requests:

// DepartmentsHttpService.js
import {HTTP} from ‘../http’;
export default { saveChanges(params) { return HTTP.post(‘departments’, params); }, };

Question quality disclaimer

If you need more of my components code I’ll update the question. Maybe it’s not perfectly formulated right now, I’m open to make edits.

Solution :

If I’m understanding the question properly, it seems easy to solve with Promises

async submitChanges() {
try {
// Store the Promise so that we can wait for it from any point in our component
this.saveChangesPromise = DepartmentsHttpService.saveChanges();
await this.saveChangesPromise;
this.hideModal();
} catch(e) {
/* show errors, log */ this.hideModal();
}
},

async handleEscapePressed() {
// If somebody has decided to save, wait for the save action to be finished before we close
if (this.saveChangesPromise){
await this.saveChangesPromise;
}
this.hideModal();
}

[Vue.js] hide .env variable values in spa apps from end users

Can a user who is using my vuejs app, see the values of .env variables or are they somehow hidden?

to keep an app-id that is part of a websocket URL private. It is probably not a problem for users to see it, but I would rather they didn’t

I could connect to the WS service via node on a backend server and pipe the data to the end user, but since when working with real-time trading data, that would double the latency to the end user.

Solution :

In short words: No.

If you need to pass data to the client you cannot hide, you could try to hide or mask, but if the code is in the client, the code is available.
Moreover, if you use it on a websocket it will be very easy to get with de Chrome Dev Tools (or any web browser dev tools):

The idea that you propose to the back-end seems to be best to hide the app-id, to reduce the latency make the middleware in the middle of the clients and the other server, I mean, if you need the data from Canada and most of the users are in Mxico i will try to put the middleware on Canad, EEUU or Mxico.

In general, I will avoid the middleware proposal excepts these cases:

the SPA limits the websocket use, i.e. you will use the websocket to get info about the Bitcoin exchange, but the websocket could use to buy or sell
If use of the original websocket cost money for you, if you have to pay for the use of the WS maybe someone take the app id to use free while you pay it (to fix this, you need a middleware and make the middleware usable only by the app)
If the data that you consume is private and you are in a public platform (I mean, if you are a in a private network like the office o VPN the security is not so important that make a web public)

Hope it helps :)
If you need more, please share more info about the project.

Image’s source

[Vue.js] How to create and publish a Vuejs component on NPM

I started working a lot with vue.js and started to use it in all the projects in the company where I work. And with that, I ended up creating some components, in general autocomplete, I know that there are many, there is already used some, but none have supplied all my needs. However, whenever I go to work on a new project and use the same component, either I recreates it, or I copy and paste it.

So I came to doubt How to create my component, upload to npmjs for whenever I use it, just give a npm install -save …, and also be able to contribute a bit with the community.

Solution :

Here is one way you can create/publish a Vuejs library/component from scratch.

As when going to write down every step and command, make sure to follow the entire guide and you will be able to create and publish the own Vuejs component on NPM.

After you publish it, like most libraries you can install it using ex:

npm install –save your-component

And then import the component inside the app using

import something from ‘your-component’

To start creating our first component, first create a folder called vuejs-hello-app (or any other name) and inside it, run:

npm init

Just hit enter until the interactive question ends and then npm will generate a file named package.json in that folder containing the following code.

(Note: I changed the description and version from 1.0.0 to 0.1.0 here is the result.)

{
“name”: “vuejs-hello-app”,
“version”: “0.1.0”,
“description”: “vuejs library demo”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \“Error: no test specified\“ && exit 1”
},
“author”: “”,
“license”: “ISC”
}

After this, we’ll need to install the dependencies for our library.

These dependencies are divided into two types: dependency and devDependency

dependency:
is the external library or libraries that our own component runs on. When someone installs the component, npm will make sure this dependency exists or gets installed first. Since we are creating a component for vue, we need to make sure vue.js is required. So, install it using:

npm install –save vue

devDependency:
is a bunch of libraries that we need only for development purposes. These libraries will help has build and/or transpile.

We install dev dependencies using the method above but adding the the suffix -dev to –save

Now, let us install the minimum dev dependencies we need for our component:

npm install –save-dev babel-core
npm install –save-dev babel-loader
npm install –save-dev babel-preset-env
npm install –save-dev cross-env
npm install –save-dev css-loader
npm install –save-dev file-loader
npm install –save-dev node-sass
npm install –save-dev sass-loader
npm install –save-dev vue-loader
npm install –save-dev vue-template-compiler
npm install –save-dev webpack
npm install –save-dev webpack-dev-server

At this point the libraries will be installed and the package.json will be updated to look like following.

{
“name”: “vuejs-hello-app”,
“version”: “0.1.0”,
“description”: “vuejs library demo”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \“Error: no test specified\“ && exit 1”,
“build”: “webpack -p”
},
“author”: “”,
“license”: “ISC”,
“devDependencies”: {
“babel-core”: “^6.26.0”,
“babel-loader”: “^7.1.2”,
“babel-preset-env”: “^1.6.1”,
“cross-env”: “^5.1.1”,
“css-loader”: “^0.28.7”,
“file-loader”: “^1.1.5”,
“node-sass”: “^4.7.2”,
“sass-loader”: “^6.0.6”,
“vue-loader”: “^13.5.0”,
“vue-template-compiler”: “^2.5.9”,
“webpack”: “^3.10.0”,
“webpack-dev-server”: “^2.9.7”
},
“dependencies”: {
“vue”: “^2.5.9”
}
}

(note: there is added “build”: “webpack -p” to build our lib with webpack)

Now, since our code needs to be build and transpiled, we need a folder to store the build version. Go ahead and create a folder inside our root folder and call it: dist and in the same place a configuration file for webpack and name it webpack.config.js

All of the files we have sofar created are for configuring and stuff. For the actual app that people are going to use, we need to create at least two files inside our src/ directory.

A main.js and VuejsHelloApp.vue.js put them as:
./src/main.js and ./src/components/VuejsHelloApp.vue

there is mine structured like this.

dist
node_modules
src
main.js
components
VuejsHelloApp.vue
.babelrc
.eslintignore
.gitignore
.npmignore
.travis.yml
CONTRIBUTING
LICENSE
package.json
README.md
webpack.config.js

I will just go through the files listed and describe what each file does in-case anyone is curious:

/dist is where a build (transpiled), minified, non-ES6 version of the code will be stores

node_modules I think we know this already, let’s ignore it

src/ this is root dir of the library.

.babelrc is where the babel options are kept, so add this to disable presets on modules

{
“presets”: [
[
“env”,
{
“modules”: false
}
]
]
}

.eslintignore This is where you tell ESLINT to ignore linting so put this inside:

build/*.js

.gitignore
add files you want to ignore (from git)

.npmignore same as .gitignore for NPM

.travis.yml if you need CI check examples from travis and configure it

CONTRIBUTING not required

LICENSE not required

package.json ignore for now

README.md not required

webpack.config.js This is the important file that let’s you create a build, browser compatible version of the code.

So, according to our app, here is a minimal example of what it should look like:

var path = require(‘path’)
var webpack = require(‘webpack’)

module.exports = {
entry: ‘./src/main.js’,

module: {
rules: [
// use babel-loader for js files
{ test: /\.js$/, use: ‘babel-loader’ },
// use vue-loader for .vue.js files
{ test: /\.vue$/, use: ‘vue-loader’ }
]
},
// default for pretty much every project
context: __dirname,
// specify the entry/main file
output: {
// specify the output directory…
path: path.resolve(__dirname, ‘./dist’),
// and filename
filename: ‘vuejs-hello-app.js’
}
}

if (process.env.NODE_ENV === ‘production’) {
module.exports.devtool = ‘#source-map’
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
‘process.env’: {
NODE_ENV: ‘“production”‘
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

Note that the important directives here are entry and output. You can check webpack docs to learn more if you want to fully customize the app.

But basically, we’re telling webpack to get the ./src/main.js (our app) and output it as ./dist/vuejs-hello-app.js

Now, we are almost finished setting up everything except the actual app.

Go to /src/components/VuejsHelloApp.vue.js and dump this simple app, which will move a button right or left when you hover on it

<template>
<div>
<button @mouseover=’move($event)’> I’m alive </button>
</div>
</template>

<script>
export default {
data () {
return {}
},

methods: {
move (event) {
let pos = event.target.style.float;
if(pos === ‘left’){
event.target.style.float = ‘right’
}else{
event.target.style.float = ‘left’
}
}
}
}

</script>

<style scoped>

</style>

And not but not least, got to ./src/main.js and export the app like:

import VuejsHelloApp from ‘./components/VuejsHelloApp.vue’
export default VuejsHelloApp

Now go to the package.json file replace the “main: “index.js”, with “main”: “src/main.js”,

After this, simply run these commands to build and publish the app:

npm run build
git add .
git commit -m “initial commit”
git push -u origin master
npm login
npm publish

Importing and using the library.

If everything went smoothly, then simply install the app like this:

npm install –save vuejs-hello-app

And use it in vue.js like this:

<template>
<div>
<VuejsHelloApp> </VuejsHelloApp>
</div>
</template>

<script>
import VuejsHelloApp from ‘vuejs-hello-app’
export default {
name: ‘HelloWorld’,
components: { VuejsHelloApp }
}
</script>

I made this app https://github.com/samayo/vuejs-hello-app while writing the answer, it might help to better understand the code

[Vue.js] VueJS How can I use computed property with v-for

How can I use computed property in lists. when using VueJS v2.0.2.

Here’s the HTML:

<div id=”el”>
<p v-for=”item in items”>
<span>{fullName}</span>
</p>
</div>

Here’s the vue.js code:

var items = [
{ id:1, firstname:’John’, lastname: ‘Doe’ },
{ id:2, firstname:’Martin’, lastname: ‘Bust’ }
];

var vm = new Vue({
el: ‘#el’,
data: { items: items },
computed: {
fullName: function(item) {
return item.firstname + ‘ ‘ + item.lastname;
},
},
});

Solution :

You can’t create a computed property for each iteration. Ideally, each of those items would be their own component so each one can have its own fullName computed property.

What you can do, if you don’t want to create a user component, is use a method instead. You can move fullName right from the computed property to methods, then you can use it like:

{ fullName(user) }

Also, side note, if you find yourself needing to pass an arguments to a computed you likely want a method instead.

Solution 2:

What you’re missing here is that the items is an array, which holds all the items, but the computed is a single fullName, which just can’t express all the fullNames in items. Try this:

var vm = new Vue({
el: ‘#el’,
data: { items: items },
computed: {
// corrections start
fullNames: function() {
return this.items.map(function(item) {
return item.firstname + ‘ ‘ + item.lastname;
});
}
// corrections end
}
});

Then in the view:

<div id=”el”>
<p v-for=”(item, index) in items”>
<span>{fullNames[index]}</span>
</p>
</div>

The way Bill introduces surely works, but we can do it with computed props and I think it’s a better design than method in iterations, especially when the app gets larger. Also, computed has a performance gain compared to method on some circumstances: http://vuejs.org/guide/computed.html#Computed-Caching-vs-Methods