I have array of cards like this
<nuxt-link :to="{ name: 'portfolio-slug', params: { slug: card.slug } }">
<a :href="card.link>Go to href</a>
</nuxt-link>
click on card with nuxt-link should opening page with details of card
click on a href should open external site
but when i clicking on a-href its open details and ignoring a-href
tried use some tags for nuxt-link but not helped
If you click that <a> inside an <a> (it's just what <nuxt-link> generates) you are actually sending the click event to both elements. That it's not good practice (even stopping the propagation with js). just don't nest it
Perhaps absolute position it with css if it has to be on top of the "card".
Try something like:
<div class="card">
<nuxt-link :to="{ name: 'portfolio-slug', params: { slug: card.slug } }">
{{ card.content }}
</nuxt-link>
<a class="card__cta" :href="card.link>Go to href</a>
</div>
and
.card {
position: relative;
}
.card__cta {
position: absolute;
bottom: 24px; // depending where you need it, maybe you need top property
right: 24px; // depending where you need it, maybe you need left property
}
This seems to work for me at this moment
In Parent component
<template>
<ul
v-if="loadedTertiaryMenu"
class="nav justify-content-end"
>
<li
v-for="item in loadedTertiaryMenu"
:key="item.id"
class="nav-item"
>
<NavLink
:attributes="item"
/>
</li>
</ul>
</template>
<script>
import NavLink from '#/components/Navigation/NavLink'
export default {
name: 'TheNavigationTertiary',
computed: {
loadedTertiaryMenu() {
return this.$store.getters.loadedTertiaryMenu
}
},
components: {
NavLink
}
}
</script>
<style scoped lang="scss">
</style>
in Child component
<template>
<component
v-bind="linkProps(attributes.path)"
:is="NavLink"
:title="attributes.title"
:class="[ attributes.cssClasses ]"
class="nav-link active"
aria-current="page"
>
{{ attributes.label }}
</component>
</template>
<script>
export default {
name: 'NavLink',
props: {
attributes: {
type: Object,
required: true
}
},
methods: {
linkProps (path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
}
</script>
<style scoped lang="scss">
</style>
It is an extension to #ArlanT answer
It adds the <slot/> so you can use it externally as follows.
<hyper-link class="anyClass" :url="myCustomUrl" #click.native="">Here lies my html</hyper-link>
<template>
<component
v-bind="linkProps(url)"
:is="'hyperLink'"
>
<slot/>
</component>
</template>
<script>
export default {
props:['url'],
name:'hyperLink',
methods: {
linkProps (path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
};
</script>
<style>
</style>
<a :href="card.url" target="_blank">{{ card.title }}</a>
Related
I want to log the value of the options on change.
The only problem is that I am using it inside string interpolation.
createStickyToolBox() {
const fonts = ['Pacifico', 'VT323', 'Quicksand', 'Inconsolata', 'Times New Roman'];
const options = fonts.map((font) => `<option value="${font}"> ${font} </option>`);
const div = document.createElement('div');
div.id = 'container';
div.innerHTML = `<select id="test" name="form-select" ${options} </select>`;
div.className = 'sticky-toolbox';
div.style = `position: absolute; top: ${topOffset}px; left: ${leftOffset}px;`;
document.getElementById('canvas-wrapper').appendChild(div);
},
In the innerHtml element is the select tag with the options defined above. How can I access the values and log them for example?
One of the super-powers of Vue.js relies on its component-oriented architecture.
While your snippet above could, in theory, work, it doesn't benefit from the advantages that Vue.js has to offer as a framework.
A much cleaner approach, would be to create a Vue.js component:
<template>
<div :style="{ top: topOffset + 'px', left: leftOffset + 'px' }" id="container">
<select id="test" name="form-select">
<option #change="log" v-for="option in options" :value="option">
{{ option }}
</option>
</select>
</div>
</template>
<script>
export default {
props: ['options', 'topOffset', 'leftOffset'];
data() {return {};},
methods: {
log(event) {
console.log(event.target.value);
}
}
};
</script>
<style scoped>
#id {
positon: absolute;
}
</style>
And, in the parent component, use the children as follows:
<template>
<div id="canvas-wrapper">
<button #click="addContainer" type="button"> Add new container </button>
<custom-container v-for="container in containers" :options="container.options" :topOffset="container.topOffset" :leftOffste="container.leftOffset"/>
</div>
</template>
<script>
import Container from './Container.vue';
export default {
components: { Container },
data() {
return {
containers: []
};
},
methods: {
addContainer() {
let newContainer = {
options: ['Pacifico', 'VT323', 'Quicksand', 'Inconsolata', 'Times New Roman'],
topOffset: 5,
leftOffset: 10,
};
this.containers.push(newContainer);
}
}
};
</script>
<style scoped>
#id {
positon: absolute;
}
</style>
index.vue -
<template>
<div>
<Header />
<div class="container">
<SearchForm />
</div>
</div>
</template>
<script>
const Cookie = process.client ? require('js-cookie') : undefined
export default {
data() {
return {
form: {
email: '',
password: ''
},
show: true
}
},
methods: {
logout() {
// Code will also be required to invalidate the JWT Cookie on external API
Cookie.remove('auth')
this.$store.commit('setAuth', {
auth: null,
user_type: null
})
}
}
}
</script>
<style>
.container {
/* margin: 0 auto; */
/* min-height: 100vh; */
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
</style>
jobs.vue -
<template>
<div>
<Header />
<SearchForm />
<b-container class="main_container">
<b-row>
<h1> Results for "{{q}}"</h1>
</b-row>
<b-row>
<ul id="array-rendering">
<li v-for="item in results" :key="item.job_id">
{{ item.job_title }}
{{ item.job_city }}
{{ item.job_state }}
{{ item.job_work_remote }}
</li>
</ul>
</b-row>
</b-container>
</div>
</template>
<script>
const Cookie = process.client ? require('js-cookie') : undefined
export default {
// middleware: 'notAuthenticated',
watchQuery: ['q'],
data() {
return {
q: null,
results: []
}
},
async fetch() {
this.q = this.$route.query.q
this.results = await this.$axios.$get('/api/job/search', {
params: {
keyword: this.q,
}
})
},
methods: {
}
}
</script>
<style>
.container {
align-items: center;
text-align: center;
}
</style>
SearchForm.vue component -
<template>
<div id='searchFormDiv'>
<b-form inline #submit.prevent="onSubmit">
<label class="sr-only" for="inline-form-input-name"> keyword</label>
<b-form-input v-model="form.keyword" id="inline-form-input-name" class="mb-2 mr-sm-2 mb-sm-0" placeholder="Job title or keyword" size="lg"></b-form-input>
<label class="sr-only" for="inline-form-input-username">location</label>
<b-input-group class="mb-2 mr-sm-2 mb-sm-0">
<b-form-input v-model="form.location" id="inline-form-input-username" size="lg" placeholder="City, state or zip"></b-form-input>
</b-input-group>
<b-button type="submit" variant="primary">Find Jobs</b-button>
</b-form>
</div>
</template>
<script>
import {
BIconSearch,
BIconGeoAlt
} from 'bootstrap-vue'
export default {
data() {
return {
form: {
keyword: '',
location: ''
}
}
},
created () {
this.form.keyword = this.$route.query.q
},
methods: {
onSubmit() {
this.$router.push({
path: 'jobs',
query: {
q: this.form.keyword
}
});
}
},
components: {
BIconSearch,
BIconGeoAlt
},
}
</script>
<style>
#searchFormDiv {
margin-top: 50px
}
</style>
The route for "http://localhost:3000/" returns the index.vue page.
In this vue page, I have a component with a search form. Once you complete these form and hit the seach button, it will re-direct to a results page
if this.form.keyword = "Data", the next URL will be "http://localhost:3000/jobs?q=Data" and it will be using the jobs.vue page.
The issue I'm running into is the CSS is not being loaded from the jobs.vue page. It's still coming from the index.vue page for some reason. If I refresh the page, then the CSS from jobs.vue is loading. I need the CSS to load from jobs.vue on the initial redirect. All of the query data is working as expected so thats a plus.
However, the following CSS is being applied from index.vue for some reason instead of the CSS from the jobs.vue page -
display: flex;
justify-content: center;
Does anyone know whats going on here? This app is SSR and not SPA.
You have to scope your css from the index.vue page to the other pages with the scoped directive (see docs https://vue-loader.vuejs.org/guide/scoped-css.html)
<style scoped>
/* local styles */
</style>
<style>
/* global styles */
</style>
You can add your global CSS in your layouts/default.vue file.
This solved the issue -
methods: {
onSubmit() {
window.location = 'http://localhost:3000/jobs?q=' + this.form.keyword;
}
},
I want Vue to recursively generate data structure with v-for where the my data is in the following format:
Data
var myObj = {
parent: {
child1: {
last_child1: {
test: null
}
}
}
}
The code in NodeTree.vue (below) has some logical issues as a result, there is no output. Can someone help me get the logic code in NodeTree.vue right
Here is the link to a working codesandbox demo
Here is my Component hierarchy:
<pre>
App.vue
|
|_Tree.vue
|
|_NodeTree.vue
</pre>
Here is my Vue SFC source:
Tree.vue
<template>
<div class="tree">
<ul class="tree-list">
<NodeTree :treeData="data" />
</ul>
</div>
</template>
<script>
import NodeTree from "./NodeTree";
export default {
props: ["data"],
components: {
NodeTree
}
};
</script>
<style>
.tree-list ul {
padding-left: 16px;
margin: 6px 0;
}
</style>
NodeTree.vue
<template>
<li>
<span class="label">{{ el }}</span>
<ul>
<li :key="idx" v-for="(item,idx) in treeData">
<mynode :key="pos"
v-for="(el, pos) in item"
:treeData="el" />
</li>
</ul>
</li>
</template>
<script>
export default {
name: "mynode",
props: {
treeData: Array
}
};
</script>
<style>
.label {
display: block;
border: 1px solid blue;
}
.bor {
border: 1px solid red;
}
</style>
App.vue
<template>
<div id="app">
<tree :data="folder_Names" >
</tree>
</div>
</template>
<script>
import Tree from "./components/Tree";
export default {
name: "App",
data() {
return {
folder_Names: {
parent: {
child1: {
last_child1: {
test: null
}
}
}
}
}
},
components: {
Tree
}
};
</script>
Any help is appreciated.
Thanks
Make sure your data is structured so you could create a recursive structure
App.vue:
<template>
<div id="app">
<tree :data="folder_Names" >
</tree>
</div>
</template>
<script>
import Tree from "./components/Tree";
export default {
name: "App",
data() {
return {
folder_Names: [
{ name: 'Parent1', children: [
{ name: 'Child1_1', children: [
{name: 'Grandchild1_1_1'},
{name: 'Grandchild1_1_2'}
]},
{ name: 'Child1_2' }
]},
{ name: 'Parent2', children: [
{ name: 'Child2_1', children: [
{name: 'Grandchild2_1_1'},
{name: 'Grandchild2_1_2'}
]},
{ name: 'Child2_2' },
{ name: 'Child2_3' },
]}
]}
},
components: {
Tree
}
};
</script>
Tree.vue
<template>
<div class="tree">
<ul class="tree-list">
<node-tree :treeData="data" />
</ul>
</div>
</template>
<script>
import NodeTree from "./NodeTree";
export default {
props: ["data"],
components: {
NodeTree
}
};
</script>
Tree-node
<template>
<ul>
<li :key="idx" v-for="(item,idx) in treeData">
{{ item.name }}
<my-node :treeData="item.children" />
</li>
</ul> </template>
<script>
export default {
name: "MyNode",
props: ["treeData"],
</script>
https://codesandbox.io/s/vue-demo-recursive-fb-filemanager-folderlist-test-k9jo5
Tried to separate out template from Vue Component as below but it does not work.
Referencing only vue.js file and not browsify.
Vue.component('my-checkbox', {
template: '#checkbox-template',
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});
<script type="text/x-template" id="checkbox-template">
<div class="checkbox-wrapper" #click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title"></div>
</div>
</script>
Or any alternate way to separate out template from vue component.
You define X-Templates in your HTML file. See below for a brief demo
// this is the JS file, eg app.js
Vue.component('my-checkbox', {
template: '#checkbox-template',
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});
new Vue({el:'#app'})
/* CSS file */
.checkbox-wrapper {
border: 1px solid;
display: flex;
}
.checkbox {
width: 50px;
height: 50px;
background: red;
}
.checkbox.checked {
background: green;
}
<!-- HTML file -->
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.min.js"></script>
<script type="text/x-template" id="checkbox-template">
<div class="checkbox-wrapper" #click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title">{{ title }}</div>
</div>
</script>
<div id="app"> <!-- the root Vue element -->
<my-checkbox></my-checkbox> <!-- your component -->
</div>
Sorry for my bad english it's not my main language!
Try it!
You need generate two file in same directory:
path/to/checkboxComponent.vue
path/to/checkboxComponent.html
In checkboxComponent.vue file
<script>
// Add imports here eg:
// import Something from 'something';
export default {
template: require('./checkboxComponent.html'),
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
}
</script>
In checkboxComponent.html file
<template>
<div class="checkbox-wrapper" #click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title"></div>
</div>
</template>
Now you need to declare this Component in same file you declare Vue app, as the following:
Vue.component('my-checkbox', require('path/to/checkboxComponent.vue').default);
In my case
I have three files with these directories structure:
js/app.js
js/components/checkboxComponent.vue
js/components/checkboxComponent.html
In app.js, i'm declare the Vue app, so the require method path need to start with a dot, like this:
Vue.component('my-checkbox', require('./components/checkboxComponent.vue').default);
Build A Single page app with vue 2 and vue-router 2
build.vue:
<style>
#build-content {
margin: 20px 20px;
}
</style>
<template>
<div id="build-content">
<h2>title</h2>
<div v-for="(buildValue, buildKey) in currentConfig">
<li v-for="(value, key) in buildValue"
is="build-item"
v-bind:buildEventId="buildKey"
v-bind:buildKey="key"
v-bind:buildValue="value"
v-on:remove="remove">
</li>
</div>
<br>
<br>
</div>
</template>
<script>
import BuildItem from './build-item.vue'
import Vue from "vue";
import qs from 'qs';
export default {
components:{ BuildItem },
data () {
return {
currentConfig: {
"1" : {
"akey" : "aValue",
"bkey" : "bValue",
"ckey" : "cValue",
},
"2" : {
"akey" : "aValue",
"bkey" : "bValue",
"ckey" : "cValue",
}
}
}
},
methods: {
remove: function (eventId, key) {
console.log(eventId + " " + key);
Vue.delete(this.currentConfig[eventId], key);
}
},
mounted: function () {
}
}
</script>
build-item.vue:
<style scoped>
.tab {
margin-right:2em
}
</style>
<template>
<div>
<br>
<span class="tab">event</span>
<Input v-model="eventId" placeholder="input..." style="width: 150px" class="tab"/>
<span class="tab">key:</span>
<Input v-model="key" placeholder="input..." style="width: 200px" class="tab"/>
<span class="tab">value:</span>
<Input v-model="value" placeholder="input..." style="width: 300px" class="tab"/>
<Button type="error" #click="remove">remove</Button>
</div>
</template>
<script>
export default {
data () {
return {
eventId: this.buildEventId,
key: this.buildKey,
value: this.buildValue,
}
},
props: {
buildEventId: {
type: String
},
buildKey: {
type: String
},
buildValue:{
type: String
}
},
methods: {
remove: function () {
this.$emit('remove', this.eventId, this.buildKey);
}
}
}
</script>
Click the first row of the list("1","akey","aValue'),but remove the third row("1","cKey","cValue") ,console.log output is correct,how to fix it?
Thanks
https://v2.vuejs.org/v2/guide/list.html#key
This default mode is efficient, but only suitable when your list
render output does not rely on child component state or temporary DOM
state (e.g. form input values).
<div v-for="(buildValue, buildKey) in currentConfig" :key="buildKey">
<li v-for="(value, key) in buildValue" :key="key"
is="build-item"
v-bind:buildEventId="buildKey"
v-bind:buildKey="key"
v-bind:buildValue="value"
v-on:remove="remove">
</li>
</div>
add :key="buildKey" and :key="key" ,Fixed the problem