Make Vue not throw error if component is not defined - vue.js

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.

Related

Vue template vs Vue jsx

could you explain Vue template vs Vue function jsx, what is different of it ? which one is good for use ?
Ex :
I have two components :
Component1.vue
<template>
<div>
<p>{{message}}</p>
</div>
<template>
<script>
export default {
name:'Component1',
data(){
return{
message:'This is component1'
}
},
}
</script>
Component2.vue
export default {
name:'Component2',
data(){
return{
message:'This is component2'
}
},
render(){
return(<p>{this.message}</p>)
}
}
Could I write like component2.vue ? How about performance of both ?
Both versions of writing the component will do the same thing. As far as the performance is considered, there would be no difference. Both are compiled into render function that returns Virtual DOM tree for your component.
The difference is the flavor and syntax of the implementation. Though with Vue, we mostly use templates as they are more readable over JSX, there are situation where JSX is more appropriate to use. For example, consider the case where you are trying to design a dynamic header component where level prop decides which <h1...h6> tag to use:
<template>
<h1 v-if="level === 1">
<slot></slot>
</h1>
<h2 v-else-if="level === 2">
<slot></slot>
</h2>
<h3 v-else-if="level === 3">
<slot></slot>
</h3>
<h4 v-else-if="level === 4">
<slot></slot>
</h4>
<h5 v-else-if="level === 5">
<slot></slot>
</h5>
<h6 v-else-if="level === 6">
<slot></slot>
</h6>
</template>
Same thing can be written more elegantly using render function or JSX:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // tag name
this.$slots.default // array of children
)
},
props: {
level: {
type: Number,
required: true
}
}
});
Also, if you are using TypeScript, JSX syntax will provide you compile-time check for validating props and attributes, though setting that with Vue 2 is quite an hassle. With Vue 3, that is much simpler.
As far as dynamic loading of component is considered, you can use built-in <component /> component with is prop within the template as:
<component v-bind:is="someComponentToRenderDynamically"></component>
So, this brings the same advantages as JSX or direct render function based component. For more documentations see:
Dynamic Components
Render Function & JSX
First of all let's see what are Template syntax and JSX:
JSX: A syntax extension for JavaScript that lets you write HTML-like markup inside a JavaScript file. Basically, JSX is a JavaScript render function that helps you insert your HTML right into your JavaScript code.
Template syntax: An HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component instance's data.
Using Vue templates is like using JSX in that they’re both created using JavaScript. The main difference is that Vue templates are syntactically valid HTML that can be parsed by spec-compliant browsers and HTML parsers.
What does it mean?
JSX functions are never used in the actual HTML file, while Vue templates are.
What is the difference? which one is better to use ?
According to the Vue.js documentation, Vue compiles the templates into highly-optimized JavaScript code.
But if you are familiar with Virtual DOM concepts and prefer the raw power of JavaScript, you can also directly write render functions instead of templates, with optional JSX support.
However, do note that they do not enjoy the same level of compile-time optimizations as templates.
So, we can conclude that writing template syntax with Vue is more optimized.
The vue template is much more readable and easier to understand than jsx functions.
It's much easier to save variables / properties and access them using "{{someVariables}}", rather than always telling a vue method to read them
In short, it's better to use the vue template because it's easier to implement dynamic pages with it (and other things).
Also, at this point it's not a very good idea to keep sending html code through methods.

VueJS SFC and DOM

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.

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…

Accessing properties/methods of a Vuejs component globally

I want to create a custom component and to use it like
<div id="app">
<router-view />
<my-custom-loader />
</div>
My component has some methods which I want to use globally anywhere like
this.$loader.show();
this.$loader.hide();
I can create the component but not sure how to inject its method/properties so that it's available everywhere.
Component create a encapsulated scope in vuejs.
AFAIK you should not be doing this as I feel this to be more like a workaround that a perfect vuejs way of doing this.
You can setup a ref on the <my-custom-loader> component.
<div id="app">
<router-view />
<my-custom-loader ref="loader"/>
</div>
Since the component looks like the child of root vue instance mounted on #app you can use it to acess $refs
var vm = new Vue({
el: '#app'
});
//my-custom-loader component's instance
var loader = vm.$rers.loader;
Then you can setup this loader which is the my-custom-loader component's instance on the Vue.prototype. This now allows you to acess $loader in any component using this.$loader
Vue.prototype.$loader = loader;
Now you can acess its methods using
this.$loader.hide();
this.$loader.show();
keep in mind the warning of using refs as mentioned in the docs
$refs are only populated after the component has been rendered, and it is not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid using $refs in templates or computed properties.
As for me I recommend you use custom events or register a global event bus or use vuex if your app is very big and has complex state management

How to register vue components

I have recently started using Vue and I was looking for getting to use Vue.Draggable on my project. Due to framework and current setup in use I haven't been able to implement single-page-component .vue-files, and have instead simply written app & components with string-templates to .js -files.
I'm unable to figure out how to include draggable to my application.
Basic structure looks like follow:
#HTML
<div id="app">
<mycomponent></mycomponent>
</div>
<script src="vuedraggable.js"></script>
<script src="component.js"></script>
<script src="app.js"></script>
--
#app.js (simply a wrapper)
var vm = new Vue({
el: '#app',
});
--
#component.js
Vue.component('mycomponent',{
template: `
<form>
<h2>{{ form.label }}</h2>
<draggable v-model="form.fields" class="dragArea">
<div v-for="field in form.fields">
<field :field="field"></field>
</div>
</draggable>
</form>
`,
data: function() {
return {
form: { ... form-structure-to-be ...}
};
},
});
With this structure I'm getting error "vue.js:440 [Vue warn]: Unknown custom lement: - did you register the component correctly?"
I haven't been able to figure out either "require" nor "import" method and assume that both require bootstrapping. Due to environment I'm working with I'm having hard time installing libraries with NPM & strapping, and I would prefer to register components manually - is this possible and how?
Could someone please give me directions ?