"Failed to generate render function" when i use tree component of ElementUI - vue.js

I want to use Element UI in IE 11,and when i user the tree component,it creates errors.
It will create the error
Failed to generate render function:
SyntaxError 'missing in'
I want to show a icon before the text.But in IE 11,it errors while Chrome ok.
Anyone can tell me the problem..thx
the whole demo is
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MLLRSJ</title>
<link rel="stylesheet" href="./Scripts/assets/Element/index.css">
<script src="./Scripts/assets/vue/vue.js"></script>
<script src="./Scripts/assets/Element/index.js"></script>
</head>
<body>
<div id='Element'>
<el-tree :data="tree" :props="treeProps">
<span slot-scope='{node,data}'>
<i class='el-icon-check'></i>
<span style="display:inline" v-html='node.label'></span>
</span>
</el-tree>
</div>
</body>
<script>
var vm = new Vue({
el:'#Element',
data:{
tree:[{
label:'1',
children:[{
label:'1-1'
}],
},
{
label:'2',
children:[
{
label:'2-1'
}
]
}
],
treeProps:{
label:'label',
children:'children'
}
}
})
</script>
</html>

Try to avoid curly braces(in slot-scope in your example) and arrow functions in inline templates if you work with IE11 (I had same problem in one of my projects):
<el-tree :data="tree" :props="treeProps">
<span slot-scope='someObject'>
<i class='el-icon-check'></i>
<span style="display:inline" v-html='someObject.node.label'></span>
</span>
</el-tree>

Related

Problem with Materialize chips-autocomplete

I have been trying to get chips autocomplete to work as part of a project.
I have stripped code right back to remove any odd effects and replicated code as on the Materialize site. As in attached code I can get autocomplete to work as expected but not with chips.
I have tried with Chrome and Edge browsers and various combinations of tags and class names but still unable to make it work.
So now I need some help!
What have I missed?
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<div class="container" >
<!-- autocomplete from materialize web site -->
<div class="row">
<div class="col s12">
<div class="input-field col s6">
<i class="material-icons prefix">textsms</i>
<input type="text" id="autocomplete-input" class="autocomplete">
<label for="autocomplete-input">from materialize web site</label>
</div>
</div>
</div>
<!-- chip autocomplete from materialize web site -->
<div class="row">
<div class="col s6">
<div id="chips-autocomplete" class="chips chips-autocomplete " ></div>
</div>
</div>
</div> <!-- container end -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
options={"data": {"abel":null,"baker":null,"charlie":null},
"placeholder":'fred'}
console.log (options)
var autoElems = document.querySelectorAll('.autocomplete');
var attemptElems = document.getElementById("attempt");
var chipsElems = document.getElementById("chips-autocomplete");
var auto = M.Autocomplete.init(autoElems, options);
var chips = M.Chips.init(chipsElems, options);
});
</script>
</body>
</html>
The data init for chips-autocomplete is incorrect. From the docs:
$('.chips-autocomplete').chips({
autocompleteOptions: {
data: {
'Apple': null,
'Microsoft': null,
'Google': null
},
limit: Infinity,
minLength: 1
}
});
With Vanilla JS:
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.chips-autocomplete');
var instances = M.Chips.init(elems, {
autocompleteOptions: {
data: {
'Apple': null,
'Microsoft': null,
'Google': null
},
limit: Infinity,
minLength: 1
}
});
});
you are simply setting data (in quotes, when there should be no quotes). It should be autocompleteOptions as an object, and then data inside this.

VueJS: Call function from bootstrap/jQuery script includes in template/layout from a component

i am very new to VueJS and want to build an Admin Dashboard for an existing bootstrap template (SB Admin Pro). I know there is a BootstrapVUE but we want to use the specified template that we purchased before. So this is not an option for me/us.
My Goal:
In our vue component we make an axios call to our backend to retrieve and show some data. If the call fails we want to call in the catch block a bootstrap function for toast to show some notification to the user (like: Error while fetching data from backend...). We included the bootstrap and jquery libraries from the template in the default index.html.
The Problem:
I don't know how to call the toasts (or other) functions from the vue component. In the template the call looks like this:
$("#toastBasic").toast("show");
Our index.html looks
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="svote UG (haftungsbeschränkt)" />
<script data-search-pseudo-elements defer src="js/font-awesome-5.11.2.min.js" crossorigin="anonymous"></script>
<script src="./js/feather.min.js" crossorigin="anonymous"></script>
</head>
<body class="nav-fixed">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
</div>
<!-- built files will be auto injected -->
<script defer src="js/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<script defer src="js/bootstrap.min.js" crossorigin="anonymous"></script>
<script defer src="js/script.js"></script>
</body>
</html>
Our vue component:
<template>
<main>
<div style="position: absolute; top: 1rem; right: 1rem;">
<!-- Toast container -->
<div style="position: absolute; bottom: 1rem; right: 1rem;">
<!-- Toast -->
<div class="toast" id="toastBasic" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
<div class="toast-header">
<i data-feather="bell"></i>
<strong class="mr-auto">Toast with Autohide</strong>
<small class="text-muted ml-2">just now</small>
<button class="ml-2 mb-1 close" type="button" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="toast-body">This is an example toast alert, it will dismiss automatically, or you can dismiss it manually.</div>
</div>
</div>
</div>
<ContentHeader title="Blank" icon="fas fa-file" subtitle="A blank page to get you started!" />
<div class="container-fluid mt-n10">
<div v-if="error" class="alert alert-danger" role="alert">
{{ error }}
</div>
<div class="row">
<Card cHeader="Eine Karte" class="col-md-12"> {{ contacts }}</Card>
</div>
</div>
</main>
</template>
<script>
import ContentHeader from '../../components/ContentHeader'
import Card from '../../components/Card'
import axios from 'axios';
export default {
name: "Contact",
components: {
ContentHeader,
Card,
},
data() {
return {
contacts: null,
error: null
}
},
mounted() {
const url = 'http://localhost:3000/v1/';
axios.get(url + 'contsact')
.then(response => {
this.contacts = response.data
console.log(response)}
)
.catch(error => {
console.log(error.response)
$("#toastBasic").toast("show");
});
}
}
</script>
In vue.config.js, specify jquery as external (this tells webpack where to provide jquery from when it's imported in any component):
configureWebpack: {
externals: {
jquery: 'window.jQuery'
}
}
Place all the <script>s you want loaded by the time Vue inits in your public/index.html page, in the <head> tag and remove their defer attribute. This includes any jquery plugin (or anything requiring/extending jquery) you might want to use in your Vue app (in your case, bootstrap.min.js).
The above will make it work when developing (in serve). You'll need to do the same for prod: Load jquery and any dependency before initing the Vue app.
Now you can safely use
import * as $ from 'jquery'
in any component.
Webpack will place in $ whatever window.jQuery is at the moment the component inits.
The above approach makes sure all required scripts are loaded before Vue inits (which is a bit extreme, but it makes sure there's no way you can call the jquery method before its dependencies are loaded).
If you don't want to wait for jquery and bootstrap.min.js to load before you init your Vue app, a trick you could use is to assign jquery from window object just before you need it:
yourAlertMethod() {
const $ = window.jQuery;
$.toast()...
}
Obviously, you no longer have to move all the scripts in <head> and to remove their defer. This second method doesn't guarantee they would have already loaded before your method is first used. But your app inits faster.
Here's a basic example.
I used the second method, codesandbox.io doesn't have support for #vue/cli v3 hence vue.config.js doesn't work as in a Vue project created with vue create. Therefore, I had to use the second method.
The full list of dependecies you need to load before you call the $(el).toast() method:
bootstrap.min.css
jquery.js
popper.js
bootstrap.min.js
(see them in public/index.html). You can copy/paste them from Bootstrap.
You can get ref of the element and pass it to jQuery
<div ref="toast" class="toast" id="toastBasic" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
import $ from 'jQuery';
$(this.$refs.toast).toast("show");
I don't recommend that though.

Vuejs input file form

In my vue-bootstrap application I would like to use hidden input file control. When I use the standard input component it works (Load 1). If i try to do the same with the reactive component form vue-bootstrap library it does not work (Load 2). I would appreciate any hints what I might be doing wrong.
app = new Vue({
el: "#app",
data: {
files: '',
}
})
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" >
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
</head>
<body>
<div id="app">
<div>
<b-button #click="$refs.fileInput1.click()"> Load 1</b-button>
<input type="file" ref="fileInput1" style="display:none;"/>
</div>
<div>
<b-button #click="$refs.fileInput2.click()"> Load 2</b-button>
<b-form-file v-model="files" style="display:none;" ref="fileInput2" />
</div>
</div>
</body>
</html>
The $refs.fileInput2 here is referring to the Vue component, not really the input element, which is actually wrapped inside a div (you could see it if you inspect the rendered element). So one thing you could do (although it's ugly and not recommended, you should at least move this to the methods section):
app = new Vue({
el: "#app",
data: {
files: '',
}
})
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css">
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<div>
<b-button #click="$refs.fileInput1.click()"> Load 1</b-button>
<input type="file" ref="fileInput1" style="display:none;" />
</div>
<div>
<b-button #click="$refs.fileInput2.$el.querySelector('input[type=file]').click()"> Load 2</b-button>
<b-form-file v-model="files" style="display:none;" ref="fileInput2" />
</div>
</div>
Although I would say you should just use the native file input element since you are hiding the <b-form-file/> anyway.
It's working fine.You may check the below fiddle.
https://jsfiddle.net/tyagdvm5/
<b-form-file style="display:none;" v-model="file2" choose-label="Attachment2"></b-form-file>
Or for accurate solution please create a fiddle and upload the link.

Leaflet map not loading in Bulma tab using Vue.js

I have an issue with loading Leaflet map using Vue.js and Bulma tab components (via Buefy).
If map is placed inside tab then it does not load all tiles until browser window is resized.
If map is placed outside of Bulma tabs component then it loads without any issue.
Calling map.invalidateSize() seems to help, but to do it automatically when tab changes I have to call it using setTimeout and put very big delay, like 1sec - which is very ugly.
How to get this working without this invalidateSize workaround?
Example with the issue: https://codepen.io/alxxnder/pen/zyYxwd
Example without the issue: https://codepen.io/alxxnder/pen/LMYEjr
Code:
new Vue({
el: '#app',
data: {
map: null,
},
methods: {
invalidateSize: function() {
this.map.invalidateSize();
}
},
mounted() {
this.map = L.map('map').setView([38.63, -90.23], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Leaflet Test</title>
<link rel="stylesheet" href="https://unpkg.com/buefy#0.7/dist/buefy.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.4/dist/leaflet.css">
</head>
<body>
<div class="section">
<div class="container" id="app">
<b-tabs position="is-centered">
<b-tab-item label="Tab 1">
<div class="section">
Tab 1
<div class="map" id="map" style="height: 400px; width: 100%"></div>
<button class="button is-info" #click="invalidateSize()">invalidateSize</button>
</div>
</b-tab-item>
<b-tab-item label="Tab 2">
<div class="section">
Tab 2
</div>
</b-tab-item>
</b-tabs>
</div>
</div>
</body>
<script src="https://unpkg.com/vue#2"></script>
<script src="https://unpkg.com/buefy#0.7/dist/buefy.min.js"></script>
<script src="https://unpkg.com/leaflet#1.3.4/dist/leaflet.js"></script>
</html>
As explained in Data-toggle tab does not download Leaflet map, the issue is caused by the fact that your map container does not have yet its full size when you initialize it. You may understand it more easily if your map had been in an initially hidden tab (e.g. in tab 2).
As for your initially active tab (i.e. tab 1), it is probable that Buefy / Bulma still takes some time to reveal the tab content.
Since there is no event when the tab transition completes, you have to wait for the transition duration before calling the invalidateSize method. In your case, 300ms seems to be fine.
Then you should also call it again when the user changes the tab (see Buefy tabs events), otherwise should the browser had changed size while your tab was hidden, the same issue would happen again.
Demo with maps in the 2 tabs:
new Vue({
el: '#app',
data: {
map: null,
map2: null,
tabMaps: []
},
methods: {
invalidateSize: function(tabIndex) {
setTimeout(() => {
if (typeof tabIndex === "number") {
this.tabMaps[tabIndex].invalidateSize();
} else {
// invalidate all maps
this.tabMaps.forEach(map => {
map.invalidateSize();
});
}
}, 300);
}
},
mounted() {
this.map = L.map('map').setView([38.63, -90.23], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);
// map2 in tab2
this.map2 = L.map(this.$refs.map2).setView([38.63, -90.23], 12);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(
this.map2
);
this.tabMaps.push(this.map); // 0
this.tabMaps.push(this.map2); // 1
this.invalidateSize();
}
})
<link rel="stylesheet" href="https://unpkg.com/buefy#0.7/dist/buefy.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.4/dist/leaflet.css">
<div class="section">
<div class="container" id="app">
<b-tabs #change="invalidateSize" position="is-centered">
<b-tab-item label="Tab 1">
<div class="section">
Tab 1
<div class="map" id="map" style="height: 400px; width: 100%"></div>
<button class="button is-info" #click="invalidateSize()">invalidateSize</button>
</div>
</b-tab-item>
<b-tab-item label="Tab 2">
<div class="section">
Tab 2
<div class="map" ref="map2" style="height: 400px; width: 100%"></div>
</div>
</b-tab-item>
</b-tabs>
</div>
</div>
<script src="https://unpkg.com/vue#2"></script>
<script src="https://unpkg.com/buefy#0.7/dist/buefy.min.js"></script>
<script src="https://unpkg.com/leaflet#1.3.4/dist/leaflet.js"></script>

vue.js2 console err is "app.js:2 Uncaught SyntaxError: Unexpected identifier"

the result should be a paragraph contain a name and button to change this name
from "adel" to "mohamed" but it is not working ,not showing any thing in the browser and i have console err is:
app.js:2 Uncaught SyntaxError: Unexpected identifier,
You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
html code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue.js course</title>
<link rel="stylesheet" href="./styles.css">
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="vue-app-one">
<greeting></greeting>
</div>
<div id="vue-app-two">
<greeting></greeting>
</div>
<script src="./app.js"></script>
</body>
</html>
app.js code
Vue.component('greeting', {
template: "<h1>hello {{ name }}. <button v-on:click="chang">change name</button></h1>",
data(){
return {
name:"adel"
}
},
methods: {
chang: function(){
this.name = 'mohamed';
}
}
});
var one = new Vue({
el: '#vue-app-one'
});
var two = new Vue({
el: '#vue-app-two'
});
It's simple. Just change the double quotation marks used in template to single quotation marks.
you can't include the same quote mark inside the string if it's being used to contain them
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Strings
template: "<h1>hello {{ name }}. <button v-on:click="chang">change name</button></h1>",
to
template: "<h1>hello {{ name }}. <button v-on:click='chang'>change name</button></h1>",
jsfiddle that works