vue babel-loader SyntaxError: Unexpected token on ":click" syntax - vue.js

I use render function like this:
.script.js:
methods: {
handleClick(data){
console.log(data)
},
render(h, { node, data, store }) {
return (
<span>
<span>
<span>{node.label}</span>
</span>
<span style="float: right; margin-right: 20px">
<a href="javascript:;"
:attr="data.id" #click="handleClick">Edit{data.id}
</a>
</span>
</span>
);
}
}
But babel encoutners error saying the :click has Unexpected token.
.vue:
<template src="./template.html"></template>
<script src="./script.js"></script>
package.json:
"vue": "^2.2.6"
"vue-router": "^2.4.0"
"element-ui": "^1.2.8",
"babel-core": "^6.24.0",
"babel-loader": "^6.4.1",
"babel-plugin-transform-vue-jsx": "^3.4.2",
"vue-loader": "^11.3.4",
"webpack": "^2.3.1",
webpack:
{
test: /\.vue$/,
loader: `vue-loader`,
options: {
loaders: {
js: 'babel-loader'
}
}
},
{
test: /\.js$/,
loader: `babel-loader`,
exclude: /(node_modules)/
}
.babelrc:
{
"presets": [
["es2015", { "modules": false }], "stage-1", "stage-2", "stage-3"
],
"plugins": [
["transform-vue-jsx"],
["transform-object-assign"],
["component", [{
"libraryName": "element-ui",
"styleLibraryName": "theme-default"
}]]
]
}
When i run gulp dist, babel throws an error like follows:
Namespaced tags/attributes are not supported. JSX is not XML.\nFor attributes like xlink:href, use xlinkHref instead.

As #Bert Evans suggested,
after re-reading the reademe docs of https://github.com/vuejs/babel-plugin-transform-vue-jsx, I figured out that i just wrote the code without understanding the syntax of vue-specific jsx syntax.
As the docs says:
Note that almost all built-in Vue directives are not supported when using JSX, the sole exception being v-show, which can be used with the v-show={value} syntax. In most cases there are obvious programmatic equivalents, for example v-if is just a ternary expression, and v-for is just an array.map() expression, etc.
The equivalent of the above in Vue 2.0 JSX is:
render (h) {
return (
<div
// normal attributes or component props.
id="foo"
// DOM properties are prefixed with `domProps`
domPropsInnerHTML="bar"
// event listeners are prefixed with `on` or `nativeOn`
onClick={this.clickHandler}
nativeOnClick={this.nativeClickHandler}
// other special top-level properties
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
key="key"
ref="ref"
// assign the `ref` is used on elements/components with v-for
refInFor
slot="slot">
</div>
)
}
So, i changed my code to
render(h, {node,data,store}) {
const link = {
href: `/#/schema-type/${data.id}`
};
return (
<span>
<span>
<span>{node.label}</span>
</span>
<span>
edit
</span>
</span>
);
}
And it works!

Try to write your html as a string:
Vue.component('my-component', {
template: '<div>A custom component!</div>'
});
It looks like you are used to React, but it write a little different.

Related

Vue select with deep nested data

I'm trying to make a v-select according to instruction on official documentation but my data is more nested than it shown in documentation, i can not show in my v-select the llcName of my data, and i'm stuck with this.
This is my html div and Vue instance with data below
<div id="vs">
<h1>Vue Select</h1>
<v-select multiple :options="options" :reduce="node=> node.llcName" label='llcName' v-model='selected' />
<pre>[[$data]]</pre>
</div>
<script>
Vue.component('v-select', VueSelect.VueSelect)
new Vue({
el: '#vs',
delimiters: ["[[", "]]"],
data: {
options: [
{
"node": {
"id": "U3VwcGxpZXJPYmplY3Q6MzA1",
"llcName": "new",
"suppPayment": {
"edges": [0]
}
}
},
{
"node": {
"id": "U3VwcGxpZXJPYmplY3Q6MzA2",
"llcName": "new2",
"suppPayment": {
"edges": [1]
}
}
},
{
"node": {
"id": "U3VwcGxpZXJPYmplY3Q6MzA3",
"llcName": "rteer",
"suppPayment": {
"edges": [2]
}
}
}
],
selected:"",
}
})
</script>
I think you should use getOptionLabel instead of label and there is a mistake in your reduce property.
<v-select
multiple
v-model='selected'
:options='options'
:get-option-label='option => option.node.llcName'
:reduce='option => option.node.llcName'/>
Fiddle

Laravel 6.4.1 + webpack + vue + scss dynamically loaded

I have laravel 6.4.1 with minor changes to the default webpack config.
I'm using vue components with scoped styling as well.
When I run npm run dev, everything works as it should. My Vue component is loaded and has styling.
When I run npm run production, my Vue component is not loaded.
Or well... The JS file is loaded, but the component never fires created or mounted and is not visible on screen and not visible in the DOM.
How do I know it's loaded then?
When I put console.log('test') above (or below) the export default it is displayed in the console.
When I remove the <style scoped lang="scss"> tag completely, my component is visible on screen as well.
I've already tried deleting parts of the styling, but it never works. Even an empty style tag will not render the component. It will only work when I fully remove it.
Ofcourse, I want to keep my styling in the component, so how can I fix this problem?
I've removed some JS from the Vue component to make it more readable and since I strongly suspect the issue in not in the JS I don't think it has any value for this issue.
webpack.mix.js
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix
.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');
if (mix.inProduction()) {
mix.version();
} else {
mix.sourceMaps();
}
// webpack.mix.js
const path = require('path'),
WebpackShellPlugin = require('webpack-shell-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
{CleanWebpackPlugin} = require('clean-webpack-plugin');
mix.webpackConfig({
plugins: [
new WebpackShellPlugin({
onBuildStart: [
'php artisan js-localization:export --quiet',
'php artisan ziggy:generate resources/js/ziggy-routes.js --quiet'
]
}),
new BundleAnalyzerPlugin({
analyzerMode: mix.inProduction() ? 'disabled' : 'server',
openAnalyzer: false
}),
new CleanWebpackPlugin({
dry: false,
cleanStaleWebpackAssets: true,
cleanOnceBeforeBuildPatterns: [],
cleanAfterEveryBuildPatterns: [
'js/chunk/*'
],
dangerouslyAllowCleanPatternsOutsideProject: true,
}),
],
resolve: {
alias: {
ziggy: path.resolve('vendor/tightenco/ziggy/dist/js/route.js'),
},
},
output: {
publicPath: '/',
chunkFilename: 'js/chunk/[name].[chunkhash].js',
},
});
Vue component
<template>
<div class="position-relative my-2" :style="{backgroundColor: properties.settings.backgroundColor}">
<block-template-content-text
v-if="canDisplayBlock(block, 'content', 'text')"
:text-config="tinyMce.text"
:block="block"
#block-change="storeBlockChange">
</block-template-content-text>
<block-template-content-text-image
v-if="canDisplayBlock(block, 'content', 'text-image')"
:text-config="tinyMce.text"
:image-config="tinyMce.image"
:block="block"
#block-change="storeBlockChange">
</block-template-content-text-image>
<div class="d-flex align-items-center justify-content-center flex-column position-absolute controls">
<i class="fas fa-2x fa-fw fa-chevron-up cursor-pointer" #click="$emit('sort-item', 'up', block)"></i>
<div>
<i class="fas fa-1x fa-fw fa-plus-circle cursor-pointer" #click="$emit('add-new-block', block)"></i>
<!-- delete section start -->
<i class="fas fa-1x fa-fw fa-trash cursor-pointer"
:id="'delete-' + block.hash"
#click="$emit('delete-block', $event, block)"></i>
<b-tooltip :target="'delete-' + block.hash"
:id="'tooltip-' + block.hash"
triggers="focus">
<button type="button"
class="btn btn-primary"
#click="$emit('cancel-delete-block', block)">
{{ Lang.get('general.buttons.cancel') }}
</button>
<button type="button"
class="btn btn-danger"
#click="$emit('delete-block', $event, block)">
<i class="fas fa-fw fa-trash"></i>
{{ Lang.get('general.buttons.delete')}}
</button>
</b-tooltip>
<!-- delete section end -->
<!-- popover start -->
<div class="popover-content" :hidden="!showPopover">
<i class="fas fa-fw fa-times close-popover cursor-pointer" #click="closePopover"></i>
<div class="form-group">
<label for="background-color">
{{ Lang.get('project.design.pages.block.settings.background-color') }}
</label>
<input type="color"
name="background-color"
id="background-color"
class="form-control"
v-model="properties.settings.backgroundColor"
value="#ffffff"
#input="setSetting('backgroundColor', $event)">
</div>
</div>
<i class="fas fa-1x fa-fw fa-cog cursor-pointer"
#click="togglePopover"></i>
<!-- popover end -->
</div>
<i class="fas fa-2x fa-fw fa-chevron-down sort-order cursor-pointer"
#click="$emit('sort-item', 'down', block)"></i>
</div>
</div>
</template>
<script>
// Bootstrap Vue
import {TooltipPlugin} from 'bootstrap-vue';
Vue.use(TooltipPlugin);
// TinyMCE editor
import 'tinymce/tinymce.min';
import 'tinymce/themes/silver/theme.min';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/link';
import 'tinymce/plugins/imagetools';
import {EventBus} from "../../vue/EventBus";
export default {
name: "display-block",
components: {
'block-template-content-text': () => import('./blocks/content/Text'),
'block-template-content-text-image': () => import('./blocks/content/TextImage'),
},
props: {
block: {
required: true,
type: Object
}
},
data() {
[...]
},
methods: {
[...]
},
mounted() {
[...]
}
}
</script>
<style scoped lang="scss">
#import "../../../sass/variables";
#import "~bootstrap/scss/mixins";
#import "~bootstrap/scss/bootstrap-grid";
#import "~bootstrap/scss/utilities/position";
#import "~bootstrap/scss/popover";
// Bootstrap Vue
#import '~bootstrap-vue/src/index.scss';
.block {
.block- {
&text {
#import '~tinymce/skins/ui/oxide/skin.min.css';
#import '~tinymce/skins/ui/oxide/content.min.css';
#import '~tinymce/skins/content/default/content.min.css';
}
}
}
.controls {
#extend .position-relative;
top: map_get($sizes, 50);
right: - map_get($spacers, 5);
transform: translateY(-50%);
&:first-child {
#extend .d-none;
}
.popover-content {
#extend .popover;
#extend .p-2;
min-width: 150px;
min-height: 150px;
.close-popover {
#extend .position-absolute;
#extend .mt-2;
#extend .mr-2;
top: 0;
right: 0;
}
input {
&[type=color] {
#extend .p-0;
width: 25px;
height: 25px;
border: none;
}
}
}
}
</style>
page.js
import {EventBus} from "../../vue/EventBus";
import {TooltipPlugin} from 'bootstrap-vue';
// import DisplayBlock from "../../components/project/DisplayBlock";
if (document.getElementById('page-editor')) {
// Bootstrap Vue
Vue.use(TooltipPlugin);
new Vue({
el: '#page-editor',
components: {
BlockModal: () => import('../../components/project/BlockModal'),
DisplayBlock: () => import('../../components/project/DisplayBlock'),
// DisplayBlock
},
data: {
[...]
},
computed: {
[...]
},
watch: {
[...]
},
methods: {
[...]
},
mounted() {
[...]
}
});
}
In your webpack file the resolver is missing for VUE.
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js'
}
}
Try by adding the above snippet.

webpack handlbars-loader: inlineRequires doesn't work with dynamic path

I have a simple partial
<span class="icon-dim_{{name}}">
<svg class="icon">
<use xlink:href="#{{name}}"></use>
</svg>
<noscript>
<img src="../../assets/{{name}}.png" alt="">
</noscript>
</span>
But i get this error message.
ERROR in ./hbs/partials/icon.hbs
Module parse failed: Unterminated string constant (10:110)
You may need an appropriate loader to handle this file type.
If I put a static path inside my partial
<img src="../../assets/myIcon.png" alt="">
it works.
What I have todo, that I can use dynamic paths inside a handlebars file?
I got help at github 😎
https://github.com/pcardune/handlebars-loader/issues/132
use chainged loaders
{
test: /\.hbs$/,
use: [
{
loader: 'handlebars-loader'
},
{
loader: 'extract-loader'
},
{
loader: 'html-loader',
options: {
interpolate: true
}
}
]
}
and then require the asset as a parameter
{{> ./partials/partial asset='${require('./assets/1.jpg')}' }}

bootstrap-vue modal not displaying

environment
dotnet core 2.1.0
"bootstrap-vue": "^2.0.0-rc.11",
"nuxt": "^1.4.1",
"vue": "^2.5.16",
"vue-axios": "^2.1.1",
"vue-router": "^3.0.1",
"vue-server-renderer": "^2.1.8",
"vue-template-compiler": "^2.5.16",
"vue-toasted": "^1.1.24",
"vuex": "^3.0.1",
"vuex-router-sync": "^4.0.1"
I can't figure out how to get a simple bootstrap-vue modal working. The modal-sample component renders, with the button visible, but when the button is clicked nothing happens (modal doesn't "show").
However, in the vue dev tools, I can see the show event was emitted. Also, if I copy and paste the code in the bootstrap-vue playground it works, so it has to be my set up. Not sure if it matters but it's also running in a dotnet core environment.
webpack.config
const VueLoaderPlugin = require('vue-loader/lib/plugin');
...
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue',
...
}
...
,
module: {
rules: [
{ test: /\.vue$/, include: /ClientApp/, use: 'vue-loader' },
{ test: /\.js$/,
include: [/ClientApp/,
require.resolve("bootstrap-vue")],
use: 'babel-loader' },
{ test: /\.css$/,
use: isDevBuild ? ['vue-style-loader', 'style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader' }) },
{ test: /\.(png|jpg|jpeg|gif|svg)$/,
use: 'url-loader?limit=25000' }
]
},
plugins: [
new VueLoaderPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
})
].concat(isDevBuild ? [
// Plugins that apply in development builds only
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
] : [
// Plugins that apply in production builds only
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin({use: 'site.css', fallback: 'vue-style-loader'})
])
}];
app-root.vue
import Swapshift from './modal-sample'
Vue.component('modal-sample', Swapshift);
modal-sample.vue
<template>
<div style="margin-top: 20px; padding: 10px;">
<b-button variant="primary" v-b-modal.newSwapShiftModal>New Swap Shift</b-button>
<b-modal id="newSwapShiftModal" title="New Swap Edit" >
<div class="d-block text-center">
<h3>Hello From My Modal!</h3>
</div>
</b-modal>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import bModal from 'bootstrap-vue/es/components/modal/modal'
import bModalDirective from 'bootstrap-vue/es/directives/modal/modal'
export default {
components: { bModal },
directives: { bModalDirective },
data() {
return {
swapshift: {
id: 1,
status: {
id: 1,
description: 'Requested'
}
}
}
},
computed: {
},
methods: {
},
async created() {
console.log('...in modal-sample1');
}
}
</script>
HTML attributes all get translated to lowercase. This includes parts of the directive attribute on your button. So what is happening is that when the v-b-modal directive receives the modifiers (ie. .newSwapShiftModal), it receives newswapshiftsodal from the browser (all attributes are always lowercased by the browser).
So you need to set the id on the modal to also be lowercase (as attribute values inside quotes retain their case).
<template>
<div style="margin-top: 20px; padding: 10px;">
<b-button variant="primary" v-b-modal.new-swap-shift-modal>New Swap Shift</b-button>
<b-modal id="new-swap-shift-modal" title="New Swap Edit" >
<div class="d-block text-center">
<h3>Hello From My Modal!</h3>
</div>
</b-modal>
</div>
</template>
Example fiddle showing case sensitivity issues with IDs: https://jsfiddle.net/4cnk28yw/
EDIT: also register the directive with the correct name (as mentioned by #ittus):
import { mapActions, mapState } from 'vuex'
import { BModal, VBModal } from 'bootstrap-vue'
export default {
components: { BModal },
directives: { 'b-modal': VBModal },
// ...
}
Directive name should be b-modal. You should try changing:
directives: {
'b-modal': bModalDirective
}

Vue 2.5: Passing prop to component. Prop gets bundled in variable name

I'm new to Vue.js and my first try is to make a simple line-chart using ChartJS (vue-chartjs bundle).
I've used the "HelloWorld.vue" as base material, and created a LineChart.js
The problem is that in HelloWorld, i got my variable called datacollection, this name gets passed into my LineChart.js. How do I fix so I dont get the variable name as an object
I get:
datacollection:
{
labels: {...},
datasets: {...}
}
I want:
{
labels: {...},
datasets: {...}
}
Thus, in my LineChart.js I need to do .datacollection. This will make my LineChart.js less reusable, since I always have to remember to name all my variables calling LineChart 'datacollection'.
LineChart.js:
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props: ['data', 'options'],
watch: {
'data': function (value) {
// I get the update from backend, but I have to use .datacollection (the variable name from the calling page)
console.log('Ändrat: ', value)
this.renderChart(value.datacollection, this.options)
}
},
data () {
return {
gradient: null,
gradient2: null
}
},
mounted () {
console.log('data in component', this.data)
/*
this.data contains datacollection: {
labels: {
...
},
datasets: {
....
}
}
This wont render any graph since I dont do .datacollection
*/
this.renderChart(this.data, this.options)
}
}
My Graph.vue page:
<template>
<div class='hello'>
<h1>{{ msg }}</h1>
<h2>Graph</h2>
<!-- this.datacollection is printed as expected, {labels: {}, datasets: {}} -->
<p>{{ this.datacollection }}</p>
<section>
<line-chart
:data='{datacollection}'
:options='{chartOptions}'
:width="400"
:height="200"
>
</line-chart>
</section>
<section>
<reactive-example></reactive-example>
</section>
</div>
</template>
<script>
export default {
name: 'Graph',
mounted: function () {
this.axios.get('graph/').then(response => {
console.log(response.data)
this.datacollection = response.data
})
},
data: function () {
return {
msg: 'Welcome to Your Vue.js App',
datacollection: {
labels: ['January', 'February'],
datasets: [
{
label: 'First',
backgroundColor: '#f87979',
data: [40, 20]
},
{
label: 'Second',
backgroundColor: '#aa7979',
data: [20, 30]
}
]
}
}
}
}
</script>
<!-- Add 'scoped' attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
My dependenicies (versions)
"dependencies": {
"axios": "^0.17.1",
"chart.js": "^2.7.1",
"vue": "^2.5.2",
"vue-axios": "^2.0.2",
"vue-chartjs": "^3.0.2",
"vue-router": "^3.0.1"
}
What do I miss?
In your template, you have the following:
<line-chart
:data='{datacollection}'
:options='{chartOptions}'
:width="400"
:height="200"
>
</line-chart>
In ES2015, {datacollection} is shorthand (see New notations in ECMAScript 2015) for creating a new object with a datacollection property with the value of datacollection as its value. In Vue, everything in the quotes of a binding is treated as a javascript expression, so in most modern browsers what that syntax does is create a new object with a datacollection property and pass that object to the component.
Instead, just remove the braces.
<line-chart
:data='datacollection'
:options='chartOptions'
:width="400"
:height="200"
>
</line-chart>