How to include inline .svg in Nuxt application - html

I want to do a common thing - include dynamically an svg HTML in Vue Nuxt application which I will able to style. To do this, I created a component but instead of image, I get a text data:image/svg+xhtml....
How to make it work?
<template>
<div v-html="src"></div>
</template>
<script>
export default {
name: 'Icon',
props: {
name: {
type: String,
required: true
}
},
computed: {
src() {
const src = require(`assets/icons/${this.name}.svg`)
return src
}
}
}
</script>

It seems like #nuxtjs/svg will do what you're trying to do. After installing it, try:
<template>
<div v-html="src"></div>
</template>
<script>
export default {
name: 'Icon',
props: {
name: {
type: String,
required: true
}
},
computed: {
src() {
const src = require(`assets/icons/${this.name}.svg?raw`)
return src
}
}
}
</script>

It's not easy and I tried a bunch of different solutions that "seemed" like they should work.
My understanding is that it comes down to how Webpack deals with "image" assets and using them inline vs stand-alone sort of changes the definition.
I've got a library of SVG icons that needed code access (eg change colors after run time) so I had to have the SVG inline. Here's where I landed, It's not ideal but it works.
Create a new component with a prop slot for SVG name. Inside that component add all your svg's as code. Wrap each in a v-if.
<template>
<div class="svgIcon">
<svg v-if="icon == 'name1'">...</svg>
<svg v-if="icon == 'name2'">...</svg>
<svg v-if="icon == 'name3'">...</svg>
<svg v-if="icon == 'name4'">...</svg>
</div>
</template>
<script>
export default {
props: ['icon']
}
</script>
Then use the component by filling the name you want in the slot
<template>
<div>
<svgIcons :icon="name1"></svgIcons>
</div>
</template>
<script>
import svgIcons from '../path/to/iconsComponent.vue'
export default {
components: { svgIcons },
...
}
</script>

Related

How can I get data from another Vue file?

As the code show below, A.vue file has element data return some number values
<template></template>
<script>
export default {
data(){
return{
element: [
{
number:'11'
}
{
number:'22'
}
]
}
}
}
</script>
Now I want to get element.length from A.vue to B.vue. Is there a way to do that? I saw a solution with button click but i dont want to use button to pass data.
B.vue file
<template>
<div>I want to get element.length here</div>
</template>
You can simply achieve it by passing prop (which contains the length of the element array) from A.vue component to B.vue component. Here is the live demo :
Vue.component('bcomponent', {
// declare the props
props: ['length'],
// just like data, the prop can be used inside templates
// and is also made available in the vm as this.message
template: '<div>Element length: {{ length }}</div>',
});
var app = new Vue({
el: '#app',
data: {
element: [{
number: '11'
}, {
number: '22'
}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- Component A -->
<div id="app">
<BComponent :length="element.length">
</BComponent>
</div>
If it's possible, just pass the data as a prop from B to A, this way you can implement any logic on the data.
If it's not, you should use vuex for data storage, so any component can access it.

Vue component using the <code> does not re-render on change of prop

I am making a code snippet that shows the code of a html element.
The snippet is suppose to change on press of buttons on other component.
The text displayed on snippet is passed as prop to the code snippet component.
<template>
<pre class="language-html" id="code-section">
<code>{{sourcecode}}</code>
</pre>
</template>
<script>
export default {
name: "CodeSnippest",
props: {
sourcecode: String,
}
}
The problem is that the display of the component does not change while the prop is changing (checked on Vue devtools).
It might be the fact that code tag contains several spans, but I'm not sure.
The actual text is not directly in code tag, but in span inside it
Here's the use of the snippet component:
<template>
<div>
<v-row class="my-5">
...
<code-snippest **:sourcecode="code"** />
</div>
</template>
<script>
import CodeSnippest from "./CodeSnippest.vue";
export default {
name: "PlayGround",
components: {
CodeSnippest,
},
data() {
return {
baseTag: "button",
baseClass: "btn",
additionalClass: "outline",
additionalColor: "red",
outline: false,
staticc: false,
text: "button",
colors: ["red", "sunset", "sea", "white", "whale"],
};
},
computed: {
code() {
return `
<${this.baseTag} class="${this.baseClass} ${
this.outline ? this.additionalClass : "bg"
}-${this.additionalColor} ${this.staticc ? "static" : ""}">${
this.text
}</${this.baseTag}>
`;
},
},
};
</script>
The data changes by buttons and checkboxes, the computed is 100% changing.
Any ideas?
Thank you!

why the html content not shown out when running

Now i use visual studio code to do my project. I can build my code without error, but when running, it no show out the content for html file, only have show css like header abd footer. i have click button on header but cannot go to other page.Here is the sample code
code in index.html
<nav>
List
New student
Student feedback
</nav>
Vue router
const router = new VueRouter({
routes: [
{ path: '/home', component: load('home') },
{ path: '/insert', component: load('insert') },
{ path: '/update/:id', component: load('update') },
{ path: '/feedback', component: load('feedback') },
{ path: '*', redirect: '/home' }
]
});
File name and type: _home.html, _insert.html, _update.html, _feedback.html
Can help me see the problem, thank you
I don't think you should edit directly to index.html as Vue is Single Page Application (SPA) framework. Instead, you should use Vue Component for each page.
This video might help you to figure out how to use Vue and Vue Router properly: https://youtu.be/nnVVOe7qdeQ
Edit:
For sake of clarity, Let me build simplified diagram of Vue project for you.
First of all, make sure you create the project via vue cli. It guides you to build your new vue project better.
Let's say we have 3 pages:
Home
About
Another
Each page has its own CSS, HTML (we call it template), and JavaScript in one file, the .vue file. To connect them, we need a first entrance, main.js. Inside of it, we can configure the router.
Inside main.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import HomePage from "./HomePage.vue";
import AboutPage from "./AboutPage.vue";
import AnotherPage from "./AnotherPage.vue";
// This is your router configuration
Vue.use(VueRouter);
const router = new VueRouter({
[
{ path: "/", component: HomePage },
{ path: "/about", component: AboutPage },
{ path: "/another", component: AnotherPage },
],
mode: "history",
});
// Initialize Vue App
new Vue({
router,
render: h => h(App),
}).$mount("#app");
Then, we need to create App.vue and put <router-view /> inside of it.
Inside App.vue source file
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
// Keep this empty. Except if you
// need to add sidebar or any else.
}
</script>
Now you're ready to create those three pages
Every pages looks like this:
<style scoped>
// Your CSS here
</style>
<template>
<div>
<!-- Your HTML here -->
</div>
</template>
<script>
export default {
data() {
return {
// Your reactive data here
}
},
mounted() {
// Your script here
},
methods: {
// Your functions here
},
}
</script>
That's all I can explain, hope it helps. If I missed something, please don't hesitate to tell me. Thank you!

Render email HTML in a VueJS component without impacting the global styles

I'm drafting a VueJS app which allows users to insert an email HTML template, do some processing and download the processed template afterwards.
I have two components and trying to render the HTML via the HTMLViewer component with v-html function as follows:
Home:
<template>
<div>
<mdb-container>
<mdb-row>
<mdb-col>
<div class="form-group">
<mdb-input
type="textarea"
label="Paste your HTML here:"
outline
:rows="25"
v-model="emailTemplate"
/>
</div>
</mdb-col>
<mdb-col>
<HTMLViewer :emailTemplate="emailTemplate" />
</mdb-col>
</mdb-row>
<mdb-row class="ml-1">
<mdb-btn outline="default">Next</mdb-btn>
<mdb-btn outline="danger" #click="clearEmailTemplate">Clear</mdb-btn>
</mdb-row>
</mdb-container>
</div>
</template>
<script>
import { mdbContainer, mdbRow, mdbCol, mdbInput, mdbBtn } from 'mdbvue';
import HTMLViewer from '../components/HTMLViewer';
export default {
name: 'HomePage',
components: {
mdbContainer,
mdbRow,
mdbCol,
mdbInput,
mdbBtn,
HTMLViewer
},
data() {
return {
emailTemplate: '',
};
},
methods: {
clearEmailTemplate() {
this.emailTemplate = null;
this.iframeVisible = false;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
HTMLViewer:
<template>
<div v-html="emailTemplate">
</div>
</template>
<script>
import { mdbContainer, mdbRow, mdbCol, mdbInput, mdbBtn } from 'mdbvue';
export default {
name: 'HTMLViewer',
components: {
mdbContainer,
mdbRow,
mdbCol,
mdbInput,
mdbBtn
},
props:['emailTemplate'],
data() {
return {
};
},
};
</script>
<style scoped>
</style>
The problem I'm facing is the fact that the email template CSS styles overwrite the ones from the parent component (Home).
I tried to use iFrame which solves this issue but also creates a lot of different problems related to interacting with the HTML template, therefore I would like to avoid iframe as much as possible.
My question is - is there a good way to make sure the inserted email template HTML does not affect the global styles?

Creating a dynamic Vue list that can be updated via custom props in HTML

New to VueJS. I am trying to build a custom ul component for a webpage that can be populated and updated via custom props (preferably string, but doesn't have to be), specifically in the HTML so that any other dev can simply use/update/add to the custom component with said prop, and it will add a new li through the addition of a second, third, fourth, etc. prop, appending the previous li. I am also struggling to see if more than one input type can be used on a custom prop. For a better explanation heres a coded example of what I currently have and what I would like to do:
Vue.component('resources', {
template: `
<!-- Resources Component -->
<div class="resources">
<div class="heading">
<p>Resources</p>
</div>
<ul class="resource-list">
<li v-for="item in items">
<a :src="item[source]">{{ item.message }}</a>
</li>
</ul>
</div>
`,
props: {
source: {
type: String,
default: "."
},
message: {
type: String
}
},
data () {
return {
items: [
{
message: {
type: String
},
source: {
type: String,
default: "."
}
}
]
}
}
});
And in my HTML the component looks like this:
<helpful-resources
message="test"
source="."
></helpful-resources>
This 1000% has a lot of issues, but ideally I would like to have something along the lines of this:
<helpful-resources
item: src="example url 1" message="test message 1"
item: src="example url 2" message="test message 2"
></helpful-resources>
With every addition of a new 'item' appending the previous list item with a new one with the ability to change the src and the message over and over again as needed for however many items are needed in the list.
Any help/clarification would be greatly appreciated. Thanks!
In the parent component:
<template>
<div class="resources">
<div class="heading">
<p>Resources</p>
</div>
<Helpful-resources :listItems="listItems"></Helpful-resources>
</div>
</template>
<script>
#import HelpfulResources from '#/path/to/HelpfulResources';
export default {
name: 'Resource',
components: {
HelpfulResources
},
data() {
return {
listItems: [
{src: 'link to item', message: 'special message'},
{src: 'link to item2', message: 'special message2'},
// More items ...
]
}
}
}
</script>
<style lang="scss">
/* styles */
</style>
Your component could be structured like this:
Helpful-resources.vue
<template>
<ul class="resource-list">
<li v-for="(item, index) in listItems" :key="'listItem-'+index">
<a :href="item.src">{{ item.message }}</a>
</li>
</ul>
</template>
<script>
export default {
name: 'helpful-resource',
props: [ 'listItems'],
data() {
return {
// More data ...
}
}
}
</script>
<style lang="scss">
/* styles */
</style>
Note this is styled in the vue-cli fashion, but you can modify it to fit your needs.
EDIT
To include it within an html file you would place your Vue components within the body, script tags just below the body tag.
<div id="app">
<resources :source="someData" :message="message" id="r"></resources>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<script>
let resources = Vue.component('resources', {
template: `<div class="resources">
<div class="heading"><p>Resources</p></div>
<ul class="resource-list">
<li v-for="(item, index) in items" :key="index"><a :href="source">{{ item.message }}</a></li>
</ul>
</div>`,
props: {
number: Number,
source: {
type: String,
default: "."
},
message: {
type: String,
default: 'No message'
},
// Example of multiple data types
propB: [String, Number]
},
data() {
return {
items: [
{
message: this.message,
source: this.source
}
]
}
}
});
new Vue({
el: '#app',
components: {
resources
},
data: {
someData: 'path/to/source',
message: 'Special Message'
},
});
</script>
Here's a link to the fiddle anyways...Fiddle
As far as updating the list goes, you could use an API call to get data asynchronously or allow users to add info via button or input and use a method. Or if you are talking strictly hardcoding extra values, other developers would add to your file...
Hopefully this helps. If not, please clarify.