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

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"

Related

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>

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

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 \\

AngularDart: using template input variables in structural directives

I have learning AngularDart. Everything went well so for. But I am stuck with structural directives : I cannot figure out how to use the template input variables to implement my own structural directive.
I read many times this document: Structural Directives.
And, although the material below refers to AngularJS, I read this questions/documents:
Angular 2: How to access Template Input Variables in Structural Directives
Angular 2 Custom Structural Directive binding using template input variable is not working
How to use Angular structural directive with multiple inputs
Or, how I wrote a customized version of ngFor
It is said that from the micosyntax declaration "let v=value", Angular creates the template variable "let-v". However, I cannot use the name "let-v" in a template since "let-v" is not a valid name for a variable.
By the way, if you look at the explanation that is given here for the directive ngFor :
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackByHeroId"
[class.odd]="odd">
({{i}}) {{hero.name}}
</div>
<template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd"
[ngForTrackBy]="trackByHeroId">
<div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</template>
You see that, inside the template, the template input variable i is called i (not let-i):
<div [class.odd]="odd">({{i}}) {{hero.name}}</div>
I tried a LOT of things within the Dart code of my structural directive. But nothing works.
I read the source code for the directive NgFor. Something potentially interesting here :
viewRef.setLocal('first', identical(i, 0));
viewRef.setLocal('last', identical(i, len - 1));
viewRef.setLocal('index', i);
viewRef.setLocal('count', len);
However, I tried that with no success.
Here is the simple code I wrote:
File: lib/src/directive_my_dummy.dart
import 'package:angular/angular.dart';
#Directive(
selector: '[myDummy]'
)
class MyDummyDirective implements OnInit {
TemplateRef _templateRef;
ViewContainerRef _viewContainer;
MyDummyDirective(TemplateRef templateRef, ViewContainerRef viewContainer) {
_templateRef = templateRef;
_viewContainer = viewContainer;
}
#Input('let-d')
List<int> d;
void ngOnInit() {
print("One instance of MyDummyDirective is instantiated.");
EmbeddedViewRef vr = _viewContainer.createEmbeddedView(_templateRef);
vr.setLocal('d', [1,2,3]);
print(d.toString());
}
}
File: lib/app_component.html
<div *myDummy="let d=data">
This is a dummy test. {{d.toString()}}
</div>
<div *myDummy="let d=[1,2,3]">
This is a dummy test. {{d.toString()}}
</div>
<div *myDummy="let d=getData()">
</div>
<div *myDummy="let d=[1,2,3]; let name='Toto'"></div>
The full code can be found here.
Can you show me a basic example that illustrates the use of the template input variables ?
First, there are two entities that we call "template":
The component template.
The (structural) directive template.
The term "input template variable" makes reference to the (structural) directive template.
I think that it would be better to use the appellation "input directive template variable".
I'll use the appellation "input directive template variable" instead of "input template variable".
The role of an input directive template variable is to configure a (structural) directive template.
Where does the value of an input directive template variable come from ?
The answer is: the value of an input directive template variable gets assigned within the directive instance. You cannot define the value of an input directive template variable directly within the component template. For example, the code <div *myDummy="let d=10"> below will NOT assign the value 10 to the variable d.
The value of the input directive template variable is assigned from within the directive instance. For example:
TemplateRef _templateRef;
ViewContainerRef _viewContainer;
// ...
_viewContainer.createEmbeddedView(_templateRef);
_viewContainer.get(0).setLocal('data', 'MyDummyDirective.data');
And you write, within the component template:
<div *myDummy="let d=data">
I give a simple example:
lib/src/directive_my_dummy.dart
#Directive(
selector: '[myDummy]'
)
class MyDummyDirective implements OnInit {
TemplateRef _templateRef;
ViewContainerRef _viewContainer;
#Input('myDummyVariable')
String variable;
MyDummyDirective(this._templateRef, this._viewContainer);
void ngOnInit() {
// WARNING: the property "variable" has no value assigned within the constructor.
_viewContainer.createEmbeddedView(_templateRef);
_viewContainer.get(0).setLocal('data', 'MyDummyDirective.data');
print('MyDummyDirective.variable = ${variable}');
_viewContainer.get(0).setLocal('var', 'This is ' + variable);
}
}
lib/app_component.html
<div *myDummy="let d=data; variable:'value from the lib/app_component.html'; let v=var">
<p>This is a dummy directive.</p>
<ul>
<li><b>d</b>=<code>{{d.toString()}}</code></li>
<li><b>data</b>=<code>{{data}}</code> (makes reference to the instance of AppComponent)</li>
<li><b>v</b>=<code>{{v}}</code></li>
</ul>
</div>
lib/app_component.dart
import 'package:angular/angular.dart';
import 'package:myapp/src/directive_my_dummy.dart';
#Component(
selector: 'app-component',
templateUrl: 'app_component.html',
directives: [MyDummyDirective],
)
class AppComponent {
List<int> getData() => [100, 200, 300];
String data = 'AppComponent.data';
}
The result:
This is a dummy directive.
<ul>
<li>d=MyDummyDirective.data</li>
<li>data=AppComponent.data (makes reference to the instance of AppComponent)</li>
<li>v=This is value from the lib/app_component.html</li>
</ul>
EDIT:
As a picture often speaks better than words...

Syntax for data binding with Index

Stuck on what is most likely just a syntax issue. I want to replace the '1' in 'play1' with the v-for index.
<tr v-for="index in 5">
<td>{{player1.round1.play1}}</td>
<td>{{player2.round1.play1}}</td>
</tr>
I tried many variations of {{player1.round1.play + index}} with no success.
<tr v-for="index in 5">
<td>{{player1.round1['play'+index]}}</td>
<td>{{player2.round1['play'+index]}}</td>
</tr>
within the double-curlies of the vue template, the content is handled as javascript.
when looking up an object in javascript you can either pass the key using the dot notation or the bracket syntax.
For example, if you have an object such as this:
const objectA = {
objectB: {
objectC: {
}
}
};
you can look up objectC either using dot notation:
objectA.objectB.objectC
or using brackets:
objectA['objectB']['objectC']
note that when you are using brackets, that you have to use a simple type, a number or a string (technically symbols are also accepted, but let's not worry about that right now). The bracket syntax does however allow you to use a variable in order to access an object, like so:
let b='objectB';
let c='C';
objectA[b]['object' + c];
objectA[b][`object${c}`];
knowing this, you can then use that to access the right object inside your vue template like this:
<td>{{player1.round1['play'+index]}}</td>
or, using template literals:
<td>{{player2.round1[`play${index}`]}}</td>

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>