VueJS SFC and DOM - vuejs2

I'm looking to try and achieve something which I believe should be possible, however, currently I've not been able to achieve it nor find any material which points in the right direction!
Ideally, I would like to build a set of components through SFC's (.vue files) and have these compiled. In terms of using these components, they should be able to be placed on an existing HTML page, and additional HTML passed into the component through the use of slot. I've already got the latter theory working through compiled SFC's and the Vue-CLI "App" component that is included. This obviously works by drawing all content into <div id="app">..., ideally I need to get away from this and end up with the following:
<body>
<div id="app">
<div class="row">
<div class="col-xs-4">
<filters></filters> <!-- "Filter" component -->
</div>
<div class="col-xs-8">
<result-list>
<div>I'm a result template!</div>
</result-list>
</div>
</div>
</div>
</body>
The overall page would be using Vuex to handle state between the two modules. Currently if I try changing main.js (included as part of vue-cli create .) from:
new Vue({
store,
render: h => h(App)
}).$mount("#app");
to:
new Vue({
store,
components: {
ResultList
},
el: "#app"
});
This results in a console error:
Unknown custom element <result-list> did you register the component correctly?
My initial reading suggests this is because everything has been compiled and so Vue can't recognise the element, but I have changed the vue.config.js file to ensure runtimeCompiler is set to true, unfortunately this does not help in the slightest.
Can anybody point me in the right direction, perhaps examples of where this approach may have been done in the past, or if I'm going completely mad and it's actually not possible?
Thanks,
Chris.

So it would appear that after actually sleeping I managed to work it out.
#skirtle you were correct in that it's because I wasn't exposing the components as global components. By changing the way in which my app mounts I was able to get the result that I needed. I'm now of the opinion when I can compile all components, but then expose those that I wish to be usable through static HTML.
new Vue({
el: '#app',
store,
components: {
'result-list': ResultList
}
})
This now allows for <result-list> to be used in the HTML dynamically within the #app container.

Related

Vue Custom directive not working with template tag

I'm facing issue with the below directive, it is working with any tag except the template tag
Vue.directive('count',{
bind(el,b,v){
console.log(b.value);
},
inserted(el,b,v){
console.log(b.value);
},
update(el,b,v){
console.log(b.value);
}
});
new Vue({
el: "#app" ,
data:{
value:0
}
})
<div id="app">
<button #click="value+=1">
inc value
</button>
<template v-count="value">
</template>
<!-- <div v-count="value">
</div> -->
</div>
If the comment on the div is removed, the directive logs the value, but with the template tag directive's hooks are not triggered
Here jsfiddle for the issue
Thanks
The template tag doesn't actually mount anything on its own, so it has no place in the DOM and doesn't bind. In your case, an easy solution is to just put v-count="value" on the button tag. If there is a more specialized need for this directive and you're just giving us placeholder code, putting it on a SPAN or DIV will make more sense than a template anyway.

Make Vue not throw error if component is not defined

I'm developing a component which have an optional child component.
<template>
<div>
<child-component :propA="valueOfA"/> <!-- this child will certainly appear -->
<optional-child-component :propB="valueOfB" /> <!-- this child may not appear -->
</div>
</template>
This is for a library I'm developing. optional-child-component comes from a 3rd-party library that the users of my library may want to use. I won't force them to install 3rd-party libraries if they don't want. The problem is that, currently, if the 3rd-party library is not installed then Vue will throw an error saying that optional-child-component is not defined.
How do I include an optional component in my code given that this component may not be defined/registered?
Not sure whether this is the best way but you could maybe check $options.components to see which components are registered.
In the example below there are 3 child components. One of them is registered globally, one is registered locally and the third isn't registered at all. That third component won't be rendered.
Vue.component('comp-a', { template: `<div>a</div>` })
new Vue({
el: '#app',
components: {
'comp-b': {
template: `<div>b</div>`
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<comp-a v-if="$options.components['comp-a']"></comp-a>
<comp-b v-if="$options.components['comp-b']"></comp-b>
<comp-c v-if="$options.components['comp-c']"></comp-c>
</div>
There may be a complication here around case, e.g. kebab-case vs PascalCase, but I doubt that would be an insurmountable obstacle to getting this to work.

Correct way to use Vue component mutiple times in one page

I am trying to create a simple 24 hour time input that I can use across multiple browsers.
In the past I have used Vue to create components which are used once on a page, and I attach them to an id as follows:
<script>
$(function () {
new Vue({
el: "#my-time-element"
});
});
</script>
But what happens when I want to use it multiple times in one page? I tried attaching it using a class definition (attach to .my-element) but it only renders for the first matching element. (The component is registered globally.)
Perhaps I am supposed to wrap the page in a single Vue component, then embed my time component in its template? Perhaps I need to make a jQuery collection of all .my-element elements, then cycle through and instantiate Vue for each one? What is the correct way to approach this?
Perhaps I am supposed to wrap the page in a single Vue component, then embed my time component in its template?
That is definitely the easiest way to do it.
You do not have to change so many things compared to your regular HTML page: simply make sure to provide an ID to one of your root container, then use your Custom Component tag anywhere within and as many times you like.
You just need to prepare Vue by globally declaring your custom component first, with Vue.component:
Vue.component('my-component', {
template: '#my-component',
});
new Vue({
el: '#app',
});
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
<h1>Hello World</h1>
<my-component>Component instance 1</my-component>
<my-component>Component instance 2</my-component>
</div>
<template id="my-component">
<div style="border: 1px solid black">
<h2>My component</h2>
<slot></slot>
</div>
</template>
Perhaps I need to make a jQuery collection of all .my-element elements, then cycle through and instantiate Vue for each one?
That is also a possible solution, but probably defeats the Vue spirit…

How to make Vue.js + Vuetify.js page elements to display only with CSS ready?

I'm creating a website with Vue.js + Vuetify.js
If I simulate "3G" in developer tools I can see all the elements of the App.vue page (toolbar, drawer,elements of routed pages) displaying without CSS during loading.
I preview it with webpack/localhost hot reload npm run dev
I want the spinner to load first and then the rest of the elements (with CSS). How do I do it properly?
I tried v-cloak and it isn't working. Looks like It's for hiding the {{ }}.
project structure
index.html |
<div id="app"></div>
App.vue |
<v-app>
<loader></loader>
<div :v-show="false">
...
<v-toolbar>...</v-toolbar>
<router-view></router-view>
</div>
</v-app>
these methods on the App.vue page doesn't seem to solve the problem:
created: function () {show: true}
mounted: function () {show: true}
P.S. I'm a newbie, so it's possible that I'm missing some small detail preventing it from working properly.
Question:
Could someone please show a proper way of doing it?
Try this way:
<v-app>
<loader></loader>
<div v-show="false">
...
<v-toolbar>...</v-toolbar>
<router-view></router-view>
</div>
</v-app>
I think the v-show didn't work, bacause you use colon before, it's like passing a dynamic prop to the component instead of using v-show directive :)

Vue.js: initialize the data object with data from the page or from v-text directive

So I'm just starting to play with Vue.js and I would like to know if there is a nice way to initialize the data object with html tags content from the page.
Basically I have a page that displays information and I would like to turn it into a tiny Vue application to avoid having a separated edit page.
So I added the form with 2 way binding which submits the values via ajax.
The thing is I really want to avoid Flash Of Uncompiled Content, but then I feel like I have to duplicate all my data, once in the html tag, once in the data object.
<div id="app">
<span v-text="prop1">This is filled by the backend</span>
<input v-model="prop1" type="text" v-cloak />
</div>
<script>
new Vue({
el: "#app",
data: {prop1: "This is filled by the backend..again"} // << Can I avoid this?
});
</script>
Could I tell Vue to get the data from the html tags since it's already there?
Thanks!
You are looking for props
Your html element would look something like
<custom-element dataToPass="dataFromHtml"></custom-element>.
Then you can access it in your vue instance via this.dataToPass.
Have a read through the documentation, there is a difference if you pass a string or an expression (from another vue instance for example).
In your example:
<div id="app">
<span prop1="{ backendVariable}" v-text="prop1"></span>
<input v-model="prop1" type="text" v-cloak />
</div>
<script>
new Vue({
el: "#app",
props: ['prop1']
});
</script>
```