Is there a way to make escaping characters work in Vue.js in this case? - vue.js

I have a string with escaping characters in it, it comes from props as a property of an object
string: "A text \"escaped\" text"
I want to display it in the template but when I'm using it like
<span v-html="prop.string"></span>
it shows up with backslashes in my template
I don't understand why this is happening and how can it be fixed? I thought v-html would display the string without \"
UPD
The only thing I came up with is to change \" to " using regex

you can use innerText or :innerText on your div like so
<div innerText="String with </> which won't be interpreted" />
or
<!-- specialText: 'String with </> which won't be interpreted', -->
<div :innerText="specialText" />
If you know that you will need special characters at the beginning or the end of the sentence, you can use ::before or ::after, special characters won't be interpreted either.
nav a::before {
content: '<';
}
nav a::after {
content: '/>';
}

v-html will not add escape characters. Your text input must have already been escaped; The source that provides the property has already escaped the text before it is sent in as a property.
You need to fix that before the value is sent in, or by unescaping in your consuming component by adding a computed property, or creating a filter.
See this pen. https://codepen.io/Flamenco/pen/xovKLq
<div v-html='message'></div>
<div v-html='message2'></div>
<div v-html='message3'></div>
<div v-html='computed3'></div>
data: () => ({
message: "Hello \"World\"",
message2: `Hello \"World\"`,
message3: 'Hello \\"World\\"', // this one is your property.
}),
computed: {
computed3() {
return this.message3.replace(/\\"/g, '"');
}
}
I recommend dealing with this by fixing the source though...

you can use double slashes like \\

Related

How to include Handlebars partial in a string? (add it to the innerHTML of a DOM Element)

Is there a way to get the "string version" of a handlebars partial to include it in the innerHTML of an HTML element?
For instance, imagine I have a ToDo list, and I want to add a task everytime I click the button "Add Task", like this:
todo_list.hbs
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
And that I have a handlebars partial in the file "task.hbs":
task.hbs
<h1 class="task-title">The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>
<script>
const button_delete_task = document.getElementById('delete-task');
button_delete_task.addEventListener('click', deleteTask);
function deleteTask () {
// delete task code here
}
</script>
My question is: How could I create a Task partial everytime the button "Add Task" is clicked? Something like this:
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += {{> Task title="A new task"}};
// More code here...
}
</script>
I have also tried enclosing the partial with backticks (`{{> Task title="A new task"}}`), and quotes ("{{> Task title='A new task'}}") as well as read many posts on this subject, but all of them use handlebars.js, not express-handlebars.
I am using express.js for the backend, and therefore, express-handlebars as the view engine. In advance, thanks a lot for your help!
I managed to solve the issue!
It turns out that enclosing the partial with backticks works! The problem was that my partial had <script></script> tags.
Imagine my task.hbs looked like this:
<div>
<script></script>
</div>
then, the processed version of todo_list.hbs would look like this:
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += `<div>
<script></script>
</div>`;
// More code here...
}
</script>
This would be valid in a normal HTML file, but it looks like handlebars process the closing script tag that is inside the string (</script>) as a normal tag, and with it, closes the <script> tag of todo_list.hbs.
The solution I found was to not use <script> tags into my partial (not a beautiful solution, but works for me!) and instead, declare the javascript code in another file, and import it into todo_list.hbs using <script> tags with the src parameter like this:
todo_list.hbs
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += `{{> Task title="New task!"}}`;
// More code here...
}
</script>
<!-- JAVASCRIPT CODE REQUIRED BY TASK PARTIAL -->
<script src="/foo/bar/partials/Task.js"></script>
Where Task.js is the file containing the javascript of the Task.hbs partial:
Task.js
const button_delete_task = document.getElementById('delete-task');
button_delete_task.addEventListener('click', deleteTask);
function deleteTask () {
// delete task code here
}
And with this changes, Task.hbs would look like this:
Task.hbs
<h1 class="task-title">The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>
You are very close to getting this to work.
As you have noted, your Handlebars is executing on the server-side. In the case of your partial, you are trying to have it render within a script block. In order for the result to be valid JavaScript, you would need have quotes around the output of the partial so that it will be a valid JavaScript string. Therefore:
todo_list.innerHTML += "{{>Task title='A new task'}}";
Which, when rendered, would result in:
todo_list.innerHTML += "<h1>The task is: A new task</h1>";
It should be noted that quotes in your partial could be problematic. For example, if the <h1> in your partial had a class <h1 class="task">, the resultant JavaScript would now be invalid because the quote after the = would be interpreted as the closing quote of the JavaScript string. Therefore, you would need to be sure to either escape the quotes in your partial or ensure they are different from those used to wrap your partial call (a single-quote ('), in this case.
todo_list.innerHTML += "<h1 class=\"task\">The task is: A new task</h1>";
Additionally, you have an inconsistency with the id of your <div>. The tag has id="todo-list" (with a dash); but your JavaScript has document.getElementById('todo_list') (with an underscore). Those will need to be consistent.
Update
As #Sharif Velásquez Alzate noted in comments, the quotes will not work when the partial contains line-breaks because JavaScript strings cannot span multiple lines (unless each line ends with a \ to signify that the text continues to the next line. However, a template literal, using back-ticks, will support text with line-breaks.
Therefore, a better solution is:
todo_list.innerHTML += `{{>Task title='A new task'}}`;

Can I pass a computed property and an object inside a v-bind:class in Vuejs?

I want to understand if I can do something like this, because I am trying but only getting erros... Forgive my bad english.
HTML file would be something like
<p :class="{mycss: isActive}, myComputedProperty" > My text </p>
and the component file would have something like
export default {
data () {
return {
isActive: true
}
},
computed: {
myComputedProperty () {
// do something
}
}
}
class value is an expression. If it doesn't make sense in raw JavaScript, it doesn't make sense there. Here comma operator is used, so the expression evaluates to myComputedProperty, and {mycss: isActive} part is discarded.
The format for combined class value is documented:
:class="[{mycss: isActive}, myComputedProperty]"
Since computed values are involved, defining the whole class object as a computed will result in cleaner template code.
I think the error is in your HTML - the comma is probably the cause. There are lots of ways to format strings, but this is one option:
<p :class="isActive ? 'mycss ' + myComputedProperty : myComputedProperty" > My text </p>

how to style html escaped data in Vue 2 or 3

I have user-generated data I'm displaying in a Vue app, so the default Vue behavior of html-escaping the data is perfect. Except, now I'd like users to be able to search that data, and I'd like to highlight the matching text in the search result. That means I need my own styling to not be escaped, even though all the original data should still be escaped.
In other words I need to apply my styling after the data has been html-escaped, eg:
1. user inputs data:
some original data that has special characters like > and <
2. Vue html-escapes this for safe display:
some original data that has special characters like > and <
3. dynamically style the search results
Eg if user searched for "original data" it becomes:
some <span class="my-highlight-style">original data</span> that has special characters like > and <
Notice how my dynamic styling was not html escaped even though the user input was.
I could of course just use v-html to bypass the html escape entirely, but then I lose all the safety and benefit of html escaping which I don't want to lose. Ideally I want to explicitly call Vue's html escape routine, then apply my styling so that it does not get escaped, then finally render all of that unescaped (since I already applied appropriate escaping programmatically).
Does Vue offer programmatic access to its html escape routine? (And I'm not talking about $sanitize which strips out special characters entirely, I want to preserve them just like normal Vue templating does). I could of course write my own escape routine, just wondered if I could leverage Vue's instead.
Vue uses the Browser's API for encoding HTML content, as mentioned here: https://v2.vuejs.org/v2/guide/security.html#HTML-content.
So, something like this should offer you the same kind of protection as Vue would from the raw user input. In the computed property, we pass the user data through the p element to encode it. Then we chain on top of that our own highlight computed property where we can inject our own HTML, and then show that with v-html.
<template>
<div id="app">
<div><label>Raw text:<br /><textarea v-model="text" cols="50" rows="10" /></label></div>
<div><label>Search for: <input type="text" v-model="search" /></label></div>
<p><label>v-html: <span v-html="text" /></label></p>
<p><label>Highlighted: <span v-html="highlight" /></label></p>
</div>
</template>
<script>
export default {
data() {
return {
text: "some original data that has special characters like > and <",
search: "original data"
}
},
computed: {
highlight() {
const html = this.safeHtml;
return html.replace(this.search, "<span class='my-highlight-style'>$&</span>");
},
safeHtml() {
var p = document.createElement("p");
p.textContent = this.text;
return p.innerHTML;
}
}
}
</script>
<style>
.my-highlight-style {
background: orange;
padding: 5px;
}
</style>

How can I add a variable to an image link in a card (vue-bootsrap)?

I'm trying to add an id taken from an API to the end of an image url so that it's a different image for each card in the loop. I think img-src might exclusively accept a string though, and v-bind exclusively an attribute
<b-card
class="sCardCourse m-3"
v-bind:header="course.title"
v-for="course in courses"
:key="course.id"
overlay
img-src="https://picsum.photos/500/300/?image=26"
img-alt="Card Image"
header-class="text-center headHeight"
>
<div class="sbtn">
<b-button class="fbtn" :to="{ name: 'viewCourse', params: { id: course.id}}" variant="warning">View</b-button>
<b-button :to="{ name: 'editCourse', params: { id: course.id}}" variant="warning">Edit</b-button>
</div>
</b-card>
I tried:
Adding it at the end with just a + (error saying + isn't accepted)
Adding {{course.id}} in place of the static number at the end of the url (error saying
Interpolation inside attributes has been removed and to use v-bind or
:id)
Using v-bind: on the img-src (error saying v-bind requires an attribute).
Adding {{v-bind:course.id}} at the end (same error as just {{course.id}})
Is it possible with the img-src property, or do I have to do it a different way?
Problem
The issue here is that with the v-bind directive (abbreviated by the colon sign) the line in between those quotes is parsed as Javascript. This means that you need a different pair of quotes to represent a string and concatenate a variable to it.
img-src="without v-bind this is just text" // OK
:img-src="with v-bind this is javascript so it gives an error" // NOT OK
:img-src="'with v-bind and single quotes this is a valid javascript string'" // OK
Solution
Using a template literal:
:img-src="`https://picsum.photos/500/300/?image=${course.id}`"
Using a string with concatenation:
:img-src="'https://picsum.photos/500/300/?image=' + course.id"

How to specify multiple dynamic attributes by single computed prop in VueJS

I have this html element:
Link text
I want to add data-tooltip and title attributes dynamically by condition:
Link text
Is there any way in VueJS to add multiple dynamic attributes at same time:
<!-- instead of this: -->
Link text
<!-- something like this: -->
<a href="javascript:" ...tooltipAttributes >Link text</a>
You could take advantage of v-bind on the DOM element you wish to apply multiple attributes to based on some dynamically changing condition.
Here's a Plunker example demonstrating how you might go about it.
Take note of the object returned:
computed: {
multiAttrs() {
return this.showAttrs ? {
'data-toggle': 'tooltip',
title: 'Some tooltip text',
} : null;
}
}
You should be able to use v-bind="tooltipAttributes"
the docs here https://v2.vuejs.org/v2/api/#v-bind have more info, but the key part is under usage
Dynamically bind one or more attributes, or a component prop to an expression.
From the Docs:
1. You can dynamically bind multiple attributes/props to a single element by using v-bind:
(no colon, no extra attribute, just v-bind)
<a href="#" v-bind="tooltipAttributes" >Link text</a>
2. And then declare the variable in the computed section:
(you can also declare it in the data section, but that would require manual direct value changes)
computed() {
return {
tooltipAttributes: {
title: 'Title',
'data-toggle': this.toggle === true && !disabled
}
}
}
Note: Attributes with dashes/hyphens - in them (e.g. data-toggle) need to be a string because Javascript doesn't recognize - as a valid symbol in variable naming.
This is THE SAME AS:
<a href="#" title="Title" :data-toggle="this.toggle === true && !disabled" >Link text</a>