I am currently having issues passing raw HTML (entity form) to a slot for syntax highlighting.
Imagine having a slot like so;
<template v-slot:code-snippet>{{ product.title }}</template>
Im receiving the error
[Vue warn]: Property or method 'product' is not defined on the instance but referenced during render
which is down to {{ product.title }} - remove this and it works fine.
Is there anyway I can tell the instance to stop trying to render these "properties" as it is to be interpreted as plain text?
regards
As already noted you can use v-pre. I would say that is the correct answer but it is worth noting that there are other ways this can be done. Perhaps if other constraints exist these could be useful.
Hack 1:
Move the troublesome string to a data property:
<template v-slot:code-snippet>{{ code }}</template>
data () {
return {
code: '{{ product.title }}'
}
}
Hack 2:
Wrap everything in an interpolation and throw in a bit of JS string escaping:
<template v-slot:code-snippet>{{ '\u007b\u007b product.title \u007d\u007d' }}</template>
Depending on the circumstances this can also be adapted to work with v-text or v-html as appropriate.
For anyone else needing help simply wrap the content going into the slot with <div v-pre>;
<template v-slot:code-snippet><div v-pre>{{ $component->renderCode() }}</div></template>
The div doesn't seem to be passed through to the slot.
Related
I have the following html div. The {{ }} represent liquid syntax and it renders server side.
<div class="two columns">
<button
data-value='{{ value }}'
class="button-selection"
:class="selectionButtonClass($event)"
#click="selectionButton($event)"
>
<span class="text">{{ value }}</span>
</button>
</div>
In a vue 3 instance I have the following method in the same page.
selectionButtonClass(el) {
console.log('checking val');
console.log(el);
}
My goal is to set a conditional class in the selectionButton method but I can't get the element to get the data attribute. The above appears in the console log as undefined. However the #click does show the event obviously it's recognize the onclick but not the class method check.
$event is only available to event handlers. #click="selectionButton($event)" defines an inline event handler, while :class="selectionButtonClass($event)" is not an event handler.
To get the element, you need to add a ref attribute to the <button>:
<button
ref="selectionButton"
data-value='{{ value }}'
class="button-selection"
:class="selectionButtonClass($event)"
#click="selectionButton($event)"
>
And access it by this.$refs.selectionButton, assuming you are using the options API. However, the ref is available only after the component is mounted. Thus you need to handle the case where the ref is null.
More on template refs: https://vuejs.org/guide/essentials/template-refs.html
Since you are using server side rendering, I think it would be better to render the value as a parameter of the selectionButton function on the server side.
I am printing some data on some condition by looping It is working fine but i feel like my approach is not correct as i am doing calculation based work inside the script tag (javascript portion below )
My for loop
<div v-for="row in cars.honda" v-if="cars.id == row.car_id" >
**<span v-show="txt=='show'">{{ cars.id == row.car_id?txt="sizes":txt="showerror"}}</span>**
<p v-if="cars.id == row.car_id" >
{{ row.car_name}}
</p>
</div>
Is it okay or good practice to assign value to txt variable inside tag as i am unable to do the same thing when I create a function in script tag It doesnt works that way as the txt variable value is not updated
No, it not recommended even the first line of code is not recommended. Using v-for and v-if together is not a good idea. You can read more about it vuejs doc
Assigning a new txt variable, that also you should generally avoid, it will hard to track if your template have more code.
Here is sample you can do it in a simple way.
<div v-for="row in cars.honda" :key="row.id">
<div v-if="cars.id == row.car_id">
<span v-if="somecondition">Show Valid Data</span>
<span v-else>Show Error</span>
<p>
{{ row.car_name}}
</p>
</div>
</div>
Generally its good practice to avoid multiple computation in the template, template are mean to be represent the data with help of directive like v-for v-if etc. They are not much responsible for computation of logic. Also use :key with v-for for better performance.
The reason why we don't use ternary operations like you have used is because it's a recipe for bugs. Everything inside the Double brackets "{{ }}" is escaped which means if you include any html tags they would be removed. Your example should be working fine but it's best practice to only stick with v-if & v-else
<span v-if="somethingIsTrue">{{showSomething}}</span>
Well, you although you could use v-if with v-for, it's not a recommended approach. Read this for more.
When used together with v-if, v-for has a higher priority than v-if. See the list rendering guide for details.
Therefore, you should always use v-for independently and then use v-if inside it to show/hide content based on some conditions.
Now coming to your question on whether it is safe to assign values in templates, the answer is no it's not, because that's just a bad syntax which is difficult to read. Ternary operator is not used to assign values like the way you have used.
Correct syntax for assigning using a ternary operator is:
let a = b=="something" ? "Hello" : "world";
Which in turn should be encapsulated within a computed property or a method to be called everywhere in vuejs.
Showing a sample approach below
Vue.config.productionTip = false
Vue.config.devtools = false
new Vue({
el: "#app",
data: {
cars: {
id: 1,
honda: [
{car_id: 1, car_name: "Honda City"},
{car_id: 2, car_name: "Honda Civic"},
],
}
},
methods: {
isSameCar(carID){
return this.cars.id ===carID ? true : false;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="row in cars.honda" :key="row.id">
<div v-if="isSameCar(row.car_id)">
<p>{{ row.car_name}}</p>
</div>
<div v-else>
<p>Not the same car, so show error here</p>
</div>
</div>
</div>
I have a simple Vue.js app, with this template:
<h1>{{ name || '<New Document>' }}</h1>
My goal is that if name is falsy, to use the text <New Document>. This is not intended to be a custom markup tag. I want Vue.js to insert this into the document:
<h1><New Document></h1>
Instead, I get this warning on the console:
[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
According to the documentation, using a pair of curly brackets, {{ and }}, means text interpolation, and the text value of that expression will be used. Instead, Vue.js seems to want to treat it as HTML.
Why is that happening? How can it be resolved?
This is a great question. Like you, I assumed that everything between the curly braces would be evaluated as an expression and injected into the template. That's what the documentation implies, and in all cases I've encountered this appears to be true... except when there could be an HTML tag in a string literal. At least, that's why my experiments seem to indicate.
Take the following example:
<h1>{{ name || '<span>hello</span>' }}</h1>
There are two ways the parser could read this:
An h1 tag containing curly braces - within those, an expression we need to evaluate later.
An h1 tag followed by the string {{ name || ', then a span, then another string ' }}.
In this case, the parser is built to choose (2), and this explains why you received a compile-time error. The parser indicated that you have a tag starting with <New Document>, but it didn't have a corresponding closing tag.
If this seems like an odd design choice, consider this code:
<h1>{{'<span>hello</span>'}}</h1>
What did the user intend here? Did he/she mean to surround a span with curly braces and quotes? Probably.
As for a solution, you could manually escape the string:
{{ name || '<New Document>' }}
Alternatively, you could solve this with a computed property, which will eschew the template parser altogether:
<template>
<h1>{{ nameOrNew }}</h1>
</template>
<script>
export default {
data() {
return {
name: false,
};
},
computed: {
nameOrNew() {
return this.name || '<New Document>';
},
},
};
</script>
Vue's template parser parses HTML first (split into tags and their content) and only then parses tags attributes and texts.
For your template it will be something like:
tag: <h1>
|
+- text: "{{ name || '"
|
+- tag: <New> (attributes: ["Document"])
|
+- text: "' }}"
You should think about template as a valid HTML first, with Vue's interpolations added later.
Documentation also states that:
All Vue.js templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers.
Ref: compiler/parser/html-parser.js
you can include all possible ignored elements in this config Vue.config.ignoredElements
Vue.config.ignoredElements = ['New Document'];
i hope it helps
The reason for the behaviour isn't clear to me, but here are two possibilities if you just need it to work:
<h1 v-if="name">{{ name }}</h1>
<h1 v-else><New Document></h1>
Or, as already pointed out in another answer:
<h1>{{ name || '<New Document>' }}</h1>
I need to loop over some tasks objects with a v-for directive.
<div v-for="(currentTask, taskName) in step.tasks">
<span>{{ currentTask.title }}</span>
<button :class="getTaskButtonProp(currentTask, 'class')" :disabled="getTaskButtonProp(currentTask, 'disabled')">{{ getTaskButtonProp(currentTask, 'caption') }}</button>
</div>
The vue instance method involved:
// …
,methods: {
getTaskButtonProp : function (task, key) {
let out = tasksStatusDescriptor[task.status][key];
// out variable manipulation …
return out;
}
}
The data involved:
Vue complains and says ReferenceError: currentTask is not defined., as if v-bind directive parsing did not grant access to the current loop scope.
Did I miss something here ? Is there some sort of special syntax here ? Or did anyone already spot a workaround ? Thank you.
EDIT
This code is perfectly fine. A missing attribute's ending double quote boundary, up in the dom tree, led to a set of errors that have now disappeared.
Recently, I've encountered a problem that caused by the computed option of vuejs.
Firstly, I use v-for to loop for an array (soloColImgs) which is created by the computed option.
my HTML
<div class="show-box" v-for="item in soloColImgs" track-by="$index">
<img v-bind:src="item.imgUrl"/>
<a v-bind:href="item.itemUrl" target="_blank"></a>
</div>
my JS
//...
computed: {
soloColImgs :function(){
//....
},
methods: {
change:function(){
this.soloColImgs.pop();
}
}
Secondly, I change the array (soloColImgs) by using pop() or splice() etc...When I look into the console, the array can change accordingly, however, the DOM does't change at all. It would be greatful if anyone can help me out of this.
The point of a computed property is that its determined solely by the function that defines it. You cannot change it directly, you must change it by acting on the dependencies. The dependencies are the properties that are used to calculate the returned value.