I have one Vue component in which any style attribute on any tag in the component template is stripped from the rendered output. Vue 2.6. No build step. The component template is defined in a js template string. Style is stripped whether it's bound or static. Style attributes on neighbouring components are rendered just fine. There are no carriage returns in my styles. Does this suggest anything to anyone ?
Here's the start of my component...
Vue.component("justif-choice", {
template: `
<div style="top:500">
qsdfqdf
</div>
`,
...
This is the line that calls my component...
<xsl:text disable-output-escaping="yes">
<justif-choice ref="justifChoice" ></justif-choice>
And here's the rendered output...
Ok found it. The style rule I was testing, top:500, needed 'px' to be valid. So it was the browser stripping it, not Vue? There may be more to this story because the same code works fine on another page, but adding 'px' has fixed it.
Related
I'm trying to develop some components that will be used by our content editors in Storyblok and there's a use case where we would like to define layout properties (using Tailwind's classes) through props that will be coming from Storyblok components.
As an example,
I am passing the width prop through storyblok giving a value of w-1/2 which is a Tailwind class. As you see on the right the class is applied just fine to the element but there's no actual impact on the page. I have tried the same with many other classes (either for background or border colors or for text styling etc, tried to use Tailwind classes as props coming from Storyblok but didn't work).
My only guess is that Nuxt is a server side application and the CSS gets compiled on build time, therefore any new class binding to the DOM will not reflect the actual CSS that they represent. Is this right? If yes, is there a way to make this happen and work?
The code for the widthSetter component is as simple as that
<template>
{{blok.width}}
<div v-editable="blok" :class="[ blok.width ]">
<component
v-for="value in blok.blocks"
:key="value._uid"
:is="value.component"
:blok="value"
/>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
blok: {
type: Object,
required: true,
},
})
</script>
You need to add Complete Class Names.
As there is no w-1/2 in your code, TW won't generate the class.
You can workaround the issue by adding the class to safelist.
Doc: https://tailwindcss.com/docs/content-configuration#safelisting-classes
module.exports = {
safelist: ['w-1/2'],
//...
}
Then w-1/2 utility will be generated regardless if it shows up in your code or not.
I have a single-file vue3 component.
Its template looks like this.
<template>
<div ref="elements"></div>
</template>
I have scoped styles in the same file:
<style scoped>
.el {
color: red;
}
</style>
Now I want to add element in the script.
<script>
export default {
name: "App",
mounted() {
const div = this.$refs.elements;
const el = document.createElement("div");
el.setAttribute("class", "el");
el.innerText = "Hello World!";
div.appendChild(el);
},
};
</script>
The result shows that the element is not styled according to the class in the scoped styles.
Is there a way to apply styling to the elements added through script, while keeping styles in the scope?
After some research this seems to be an answer.
Vue scoped styling is the internal Vue feature. So, there should be a way to distinguish same class names on different components. Vue does it by adding a special id to each class name. You can find it by inspecting elements in the browser (e.g. data-v-a2c3afe).
When building a component, Vue processes its template and properly tracks only nodes you have declared for rendering there. It does not know anything it might get from somewhere. It actually makes sense and pushes you to write everything you expect to see in the template, so that it does not happen that you suddenly see something wrong in the DOM (especially if you are taking someone's code).
I have rewritten code, so that I still have scoped styles, but with no elements appending from the script and the code now allows to clearly see what is being rendered. This is probably the property of any framework - it makes you to stick to some patterns, so that everyone in the team/community knows what behavior to expect and writes more consistent code.
I ran into a little problem trying to access subcomponents in vue 2.5.2 while using Typescript 2.4.2.
I am trying to access a Keen UI UiSelect component by adding it to $ref with the ref="selectfield" attribute and then access it in my Vue component with this.$refs.selectfield.
The UI-Select component is unrendered with the v-if="showSelect" directive.
onClickSpan(){
showSpan = false;
showSelect = true;
console.dir(this.$refs);
console.dir(this.$refs.selectfield);
}
I have referenced two different elements with ref.
I have already tried outputting this.$refs in the console and the Object selectfield is definitely there:
>{…}
|> currentNumField: <div style="width: 100%;">
|> selectfield: Object { _uid: 32, _isVue: true, "$options": {…}, … }
|> __proto__: Object { … }
undefined
Unfortunately trying to access it will always tell me that this.$refs.selectfield is undefined.
I am pretty new to Vue and Typescript and just don't know what could be causing this problem.
Thank you for your time.
You must give a little more information please.
Look at the example
https://jsfiddle.net/Jubels/50wL7mdz/78450/
this.$refs.paraG would return the node.
this.$refs.custom would return the vue component
<p ref="paraG">{{ message }}</p>
<custom-component ref="custom">{{ message }}</custom-component>
Hope this clear any uncertainty
I am not certain where the problem came from, but for everyone having similar issues here is a possible solution:
Use v-show instead of v-if.
It seems as though the problem is related to the way the DOM is rendered by using v-if.
The ref to the DOM Element is added to the $refs attribute of my component, but the variable $refs.mycomponent is empty since an element that is hidden with v-if is not only not rendered but not even added to the DOM.
I tried using a delay after making the object visible, but that did not solve the problem.
I can only assume that this is due to an issue with typescript that doesn't properly update the $refs attribute whenever the DOM is rerendered since this works with v-if when I am only using Vue.
I'm rendering some HTML in a v-for
But everytime I change any of the data, all my html gets replaced (input fields lose their values)
I tried giving the :key all kinds of different values
I didn't have this problem in vue v1, only in v2
http://jsbin.com/jomuzexihu/1/edit?html,js,output
I had a little play with this and it appears that Vue does not re-render the entire list when using <input /> or if you use a component but it does with v-html. Heres the fiddle for the comparison:
https://jsfiddle.net/cxataxcf/
The key actually isn't needed here because the list isn't being re-ordered, so your issue isn't to do with :key but rather with v-html. Heres what the docs say about v-html:
The contents are inserted as plain HTML - data bindings are ignored. Note that you cannot use v-html to compose template partials, because Vue is not a string-based templating engine. Instead, components are preferred as the fundamental unit for UI reuse and composition.
So I guess this is where the problem lies.
It might be worth raising an issue on Vue's github page to see whether this is the expected behavior for v-html, but Vue 2.0 is much more heavily focused on components than vue 1.x and doesn't appear to recommend using v-html, so it may just be that you need to re-factor your code to use components instead.
Edit
The solution to this problem is to simply wrap the code in a component and pass the HTML as a prop:
Vue.component('unknown-html', {
props: {
html: ""
},
template: '<div><div v-html="html"></div>'
})
The markup:
<unknown-html :html="thing.html"></unknown-html>
And the View model:
var app = new Vue({
el: '#app',
data: {
numInputs: 1,
stuff: [{
'html':'<input />'
}, {
'html':'<button>Foo</button>'
}]
}
})
Here's the JSFiddle: https://jsfiddle.net/wrox5acb/
You are trying to inject raw html directly into the DOM. Probably it was possible in earlier versions of Vue.js, but it is definitely not the recommended way.
You can instead have an array of objects and bind it to html as shown in this jsFiddle: https://jsfiddle.net/43xz6xqz/
Vue.js version: 2.0.3
In the example above, vue.js is responsible for creating the input elements and also for binding these input elements to the object values using v-model.
To extract these values, you may use a computed property as shown in the sample code.
I guess, for performance optimization, when the key is not change, Vue will not rerender the dom, but will update the data import through directive.So when your input element is import through an directive (v-html), it will be rerendered everytime when stuff changes.
Due to the vue is not a string template engines, but template based on dom, so in the case of #craig_h 's example , to use the incomming html in a string template within a component:
Vue.component('unknown-html', {
props: {
html: ""
},
template: '<div><div v-html="html"></div>'
})
view:
<unknown-html :html="thing.html"></unknown-html>
So when the stuff changes, it will not to rerender the template declare in string, for vue is not a string template engine.
When should I use a directive vs a component in vue.js? I'm implementing some stuff from Bootstrap and it looks like I could do it either way (I'm starting with the dropdown menu).
I get the feeling that a directive is more for manipulating the dom on a single element, while components are for packaging a bunch of data and/or dom manipulation. Is this a good way to look at it?
This Stack Overflow question is the #1 result to the Google query "vue directive vs component". Saurshaz’s answer is currently the accepted one and it’s very wrong in Vue 2.0. I imagine this is leading a lot of people astray so I'm going to weigh in here.
The answer to “should I use a directive or a component in Vue” is almost always a component.
Do you want to have reusable HTML? I.e. reusable widgets? Then use a component. Do you want two of these widgets to have discrete data? Then use a component. The data of one will NOT override the data of another. Maybe that was true in Vue 1.0, I don't know. But it's absolutely not true in Vue 2.0. In Vue 2.0, your components have a data function that returns a unique set of data. Consider this real-life of a Vue dropdown that has an HTML markup similar to the UI Bootstrap dropdown:
<template>
<span class="dropdown sm-dropdown" #click="toggle" :class="{'open': isOpen}">
<a class="dropdown-toggle">
<span class="special-field">{{ label }}</span>
</a>
<ul class="dropdown-menu">
<li v-for="choice in choices">
<a #click.prevent="click(choice)">{{ choice.label }}</a>
</li>
</ul>
</span>
</template>
<script>
export default {
name: 'Dropdown',
props: ['label', 'options', 'onChange'],
data() {
return {
choices: this.options,
isOpen: false
}
},
methods: {
click(option) {
this.onChange(option);
},
toggle() {
this.isOpen = !this.isOpen;
}
}
}
</script>
Now in a parent component, I can do something like this:
<template>
<div class="container">
<dropdown
label="-- Select --"
:options="ratingChoices"
:onChange="toggleChoice"
>
</dropdown>
<dropdown
label="-- Select --"
:options="ratingChoices"
:onChange="toggleChoice"
>
</dropdown>
</div>
</template>
<script>
import Dropdown from '../dropdown/dropdown.component.vue';
export default {
name: 'main-directive',
components: { Dropdown },
methods: {
toggleChoice(newChoice) {
// Save this state to a store, e.g. Vuex
}
},
computed: {
ratingChoices() {
return [{
value: true,
label: 'Yes'
}, {
value: false,
label: 'No'
}]
}
}
}
</script>
There's a decent amount of code here. What's happening is we're setting up a parent component and inside that parent component we have two dropdowns. In other words, the dropdown component is being called twice. The point I'm trying to make in showing this code is this: when you click on the dropdown, the isOpen for that dropdown changes for that directive and for that directive only. Clicking on one of the dropdowns does not affect the other dropdown in any way.
Don't choose between components or directives based on whether or not you're wanting discrete data. Components allow for discrete data.
So when would you want to choose a directive in Vue?
Here are a couple of guidelines that'll hopefully get you thinking in the right direction.
You want to choose a directive when you're wanting to extend the functionality of HTML components and you suspect that you’re going to need this extendability across multiple components and you don't want your DOM to get deeper as a result. To understand what I mean by this, let's look at the directives that Vue provides out of the box. Take its v-for directive for instance. It allows you to loop through a collection. That's very useful and you need to be able to do that in any component you want, and you don't want the DOM to get any deeper. That's a good example of when a directive is the better choice.[1]
You want to choose a directive when you want a single HTML tag to have multiple functionality. For example, an element that both triggers an Ajax request and that has a custom tooltip. Assuming you want tooltips on elements other than Ajax-triggering elements, it makes sense to split these up into two different things. In this example I would make the tooltip a directive and the Ajax feature driven by a component so I could take advantage of the built-in #click directive that’s available in components.
1 A footnote for the more curious. In theory v-for could have been made as a component, but doing so would have required a deeper-than-necessary DOM every time you wanted to use v-for as well as a more awkward syntax. If Vue had chosen to make a component out of it, instead of this:
<a v-for="link in links" :href="link.href">link.anchor</a>
The syntax would have had to have been this:
<v-for items="link in links">
<a :href="link.href">link.anchor</a>
</v-for>
Not only is this clumsy, but since the component code would have needed to implement the <slot></slot> syntax in order to get the innerHTML, and since slots cannot be immediate children of a <template> declaration (since there's no guarantee that slot markup has a single node of entry at its top level), this means there would have to be a surrounding top-level element in the component definition for v-for. Hence the DOM would get deeper than necessary. Directive was unequivocally the right choice here.
I think of it this way:
Components define widgets - these are sections of html that have behavior associated with them.
Directives modify behavior of sections of html (which may or may not be widgets).
I think this difference is better explained with two examples.
Components: are wrappers that are best suited when you need to insert (or add) your own HTML tags over something to render it. E.g. a widget, a custom button, etc where you would need to add some HTML tags to show it properly.
Directives: don't add tags but rather give you direct access to the HTML tag (to which you have added the directive). This gives you access to modify the attributes of that HTML element directly. E.g. initializing a tooltip, set css styles, bind to an event, etc.
Reusability is a reason for using directives,
While Components are also creating reusable 'widgets', two components in the same html system would overwrite the previous ones 'data', So think of directives in a case like this.
Another point worth thinking of is - Can user be using it via HTML only after some instructions ?