I've created a third party components library as described on this page https://nuxtjs.org/blog/improve-your-developer-experience-with-nuxt-components#third-party-component-library. Than I used the components in a new clean nuxt project. I have a BaseCard component which has 3 slots and I have a BaseImage component. Now I want to use the BaseImage component in a slot from the BaseCard component but it is not rendered. If I add an additional BaseImage component outside of the BaseCard component, than all BaseImage components are rendered (see screenshots below). Seems like that the components within a slot are not loaded.
Screenshots
without additional BaseImage
with additional BaseImage
Code
Don't work
<template>
<div>
<BaseCard>
<template v-slot:image>
<BaseImage
imgSrc="https://picsum.photos/400/400?random=1"
imgAlt="Some alt tag"
/>
</template>
<template v-slot:header>
Here might be a page title
</template>
<template v-slot:content>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eum
pariatur distinctio cum. Ratione doloribus asperiores eaque
laboriosam repellendus perferendis iusto magni in necessitatibus
exercitationem eum expedita aliquam autem, tenetur itaque.
</p>
</template>
</BaseCard>
</div>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({});
</script>
works
<template>
<div>
<BaseCard>
<template v-slot:image>
<BaseImage
imgSrc="https://picsum.photos/400/400?random=1"
imgAlt="Some alt tag"
/>
</template>
<template v-slot:header>
Here might be a page title
</template>
<template v-slot:content>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eum
pariatur distinctio cum. Ratione doloribus asperiores eaque
laboriosam repellendus perferendis iusto magni in necessitatibus
exercitationem eum expedita aliquam autem, tenetur itaque.
</p>
</template>
</BaseCard>
<BaseImage
imgSrc="https://picsum.photos/400/400?random=1"
imgAlt="Some alt tag"
/>
</div>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({});
</script>
nuxt.config.js (shared-components is my library for the components)
export default {
// Target: https://go.nuxtjs.dev/config-target
target: "static",
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: "demo",
htmlAttrs: {
lang: "en",
},
meta: [
{ charset: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{ hid: "description", name: "description", content: "" },
],
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: ["#/assets/scss/variables.scss"],
styleResources: {
scss: ["./assets/scss/*.scss"],
},
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/typescript
"#nuxt/typescript-build",
"shared-components",
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: ["#nuxtjs/style-resources"],
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {},
};
I use nuxt 2.15.2.
Related
I have some HTML string that I get via API. Let’s say it looks like:
const msg = 'Lorem ipsum dolor sit google
consectetur adipiscing elit yahoo
lorem ipsum.'
Now I need to add Vues #click event on those a elements. Is it possible to parse somehow the string and add the Vue event on it and output it?
Basically I have an electron app and I want to some additional logic on the links other then redirect.
You do not need to use v-html here. You can parse the string easily using node-html-parser. Then you can use a v-for to show the html
Codesandbox
Code:
<template>
<div id="app">
<div>a tags go below</div>
<div v-for="(tag, key) of a_tags" :key="key">
<a :href="tag.attributes.href">{{ tag.text}}</a>
</div>
<button #click="showTags">show tags</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
import { parse } from "node-html-parser";
const msg =
'Lorem ipsum dolor sit google consectetur adipiscing elit yahoo lorem ipsum.';
const tags = parse(msg);
export default {
name: "App",
components: {
HelloWorld
},
mounted() {
console.log(tags);
},
data() {
return {
a_tags: [tags.childNodes[1], tags.childNodes[3]]
};
},
methods: {
showTags() {
console.log(this.a_tags);
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
EDIT AFTER CHECKING THE FIDDLE:
It seems that appending the refs to the string is not working, therefore, you should put a ref to the parent as follows:
<div id="app">
<div v-html="msg" ref="msg">
</div>
</div>
And then add the event to each of its two children in this case as follows:
new Vue({
el: "#app",
data: {
msg: 'Lorem ipsum dolor sit google consectetur adipiscing elit yahoo lorem ipsum.'
},
mounted() {
this.$refs.msg.children[0].onclick = function(e) {
e.preventDefault();
alert('foo')
};
this.$refs.msg.children[1].onclick = function(e) {
e.preventDefault();
alert('bar')
};
}
})
Is there a way to prevent the v-tabs from actually changing when being clicked on?
In my case I first need to check if stuff on the page has changed and want to cancel the switch to another tab if it has.
Neither a event.prevent nor event.stop will stop the v-tabs from changing:
<v-tab #click.prevent.stop="..."> ... </v-tab>
At the moment I'm using a window.requestAnimationFrame to reset the tab index to the old value. It gets the job done but this feels like a really nasty technique to me.
HTML:
<v-tabs v-model="currentIndex">
<v-tab v-for="(route, index) in list" :key="index" #change="handleTabChange(route, $event)" >
{{ route.meta.title }}
</v-tab>
</v-tabs>
TS:
public handleTabChange(routeConf:RouteConfig):void {
let currentIndex:number = this.currentIndex;
window.requestAnimationFrame(() => {
this.currentIndex = currentIndex;
Store.app.router.goto(routeConf.name, null, this.$route.params);
// Once the page actually changes this.currentIndex is set to the correct index..
});
}
I solve this problem by using separate variable between v-tabs and v-tabs-items.
<v-tabs v-model="tab" #change="onTabChange">
<v-tab v-for="item in items" :key="item">
{{ item }}
</v-tab>
</v-tabs>
<v-tabs-items v-model="currentTab">
<v-tab-item v-for="item in items" :key="item">
<v-card>
<v-card-text>{{ item }}</v-card-text>
</v-card>
</v-tab-item>
</v-tabs-items>
methods: {
onTabChange() {
if (/* reject */) {
this.$nextTick(() => {
this.tab = this.currentTab
})
} else {
this.currentTab = this.tab
}
}
}
Demo
Another possible solution is to extend the v-tab component which is a bit more complicated but can actually override the behavior.
Create new file my-tab.js:
import { VTab } from 'vuetify/lib'
export default {
extends: VTab,
methods: {
async click (e) {
if (this.disabled) {
e.preventDefault()
return
}
// <-- your conditions
let ok = await new Promise(resolve => {
setTimeout(() => {
resolve(false)
}, 2000)
})
if (!ok) {
this.$el.blur()
return
}
// -->
if (this.href &&
this.href.indexOf('#') > -1
) e.preventDefault()
if (e.detail) this.$el.blur()
this.$emit('click', e)
this.to || this.toggle()
}
}
}
The original source code is here. You can also override the render function to change the styles.
Then just use it as normal component:
<v-tabs v-model="tab">
<my-tab v-for="item in items" :key="item">
{{ item }}
</my-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item v-for="item in items" :key="item">
<v-card>
<v-card-text
>{{ item }} ipsum dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.</v-card-text
>
</v-card>
</v-tab-item>
</v-tabs-items>
For me it works the following way:
...
<v-tab href="#tab-1">Tab-1</v-tab>
<v-tab href="#tab-2" #click.native.prevent.stop.capture="goto2()">Tab-2</v-tab>
...
...
private goto2() {
if(tab-1Changes) {
// do something
return;
}
this.tab = "tab-2";
}
You should have to follow this way in your code this is example which will help you:
In ts file:
<template>
<v-tabs v-model="activeTab">
<v-tab v-for="tab in tabs" :key="tab.id" :to="tab.route">{{ tab.name }}
</v-tab>
<v-tabs-items v-model="activeTab" #change="updateRouter($event)">
<v-tab-item v-for="tab in tabs" :key="tab.id" :to="tab.route">
<router-view />
</v-tab-item>
</v-tabs-items>
</v-tabs>
</template>
Script:
export default {
data: () => ({
activeTab: '',
tabs: [
{id: '1', name: 'Tab A', route: 'component-a'},
{id: '2', name: 'Tab B', route: 'component-b'}
]
}),
methods: {
updateRouter(val){
this.$router.push(val)
}
}
}
Create a screen "custom-screen" extended from screen widget with a template show a paragraph of lorem and return object of screen widget so it can be called from other widgets.
This screen I want to trigger this screen widget from a button. The screen showed but I got this error "this.pos is undefined" when the button clicked
TypeError: this.pos is undefined
TypeError: this.pos is undefined
http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:337
Traceback:
show#http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:337:1
show_screen#http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:316:28
button_click#http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:572:385
renderElement/<#http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:362:203
dispatch#http://localhost:8069/web/content/941-9a091d9/web.assets_common.js:892:378
$event.dispatch#http://localhost:8069/web/content/1394-95d31f5/point_of_sale.assets.js:480:8
add/elemData.handle#http://localhost:8069/web/content/941-9a091d9/web.assets_common.js:865:151
__mainfest__.py
{
'name': "custom-screen",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
# always loaded
'data': [
'views/templates.xml',
],
'demo': [
'demo/demo.xml',
],
'qweb': [
'static/src/xml/custom-screen.xml',
],
}
views/templates
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/custom-screen/static/src/js/custom.js"></script>
</xpath>
</template>
</odoo>
> custom.js
odoo.define('custom-screen.custom-screen', function (require) {
"use strict";
var screens = require('point_of_sale.screens');
var gui = require('point_of_sale.gui');
var Button = screens.ActionButtonWidget.extend({
template: 'Button',
button_click: function () {
var self = this;
console.log('Button Clicked');
self.gui.show_screen('custom-screen');
},
});
screens.define_action_button({
'name': 'button',
'widget': Button,
});
var CustomScreenWidget = screens.ScreenWidget.extend({
template: 'CustomScreenWidget',
init: function () {
console.log("Initialize the custom screen");
}
});
gui.define_screen({
'name': 'custom-screen',
'widget': CustomScreenWidget,
});
return {
Button: Button,
CustomScreenWidget: CustomScreenWidget
};
});
static/src/xml/custom-screen.xml
<t t-name="Button">
<span class="control-button">
<!--<i class="fa fa-print"></i>-->
Open Custom Screen
</span>
</t>
<t t-name="CustomScreenWidget">
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus alias, aliquid cupiditate dignissimos, doloribus, enim error eum fugiat id nisi odit quibusdam quo repellat repellendus sed vitae voluptatem. Distinctio, nemo.</p>
</div>
</t>
Can you provide whole traceback of error stack here. Also this error is raised because you try to access current value of 'pos' object but it is not defined so this error is getting raised.
Could you help me identify this issue please.
I try to load remote data by clicking on the button but I have an error:
Uncaught Error: Cannot find module 'http'...
I have written couple tests with tiny-json-http and with axios.
My steps after creating a "vue-cli-error-http" project folder :
$ npm install -g vue-cli
$ cd vue-cli-error-http
$ vue init webpack .
$ npm install axios --save
$ npm install tiny-json-http --save
$ npm run dev
I created a new component LoaderTiny.vue in src/components/:
<template>
<div class="hello">
<h1>{{msg}} </h1>
<hr />
<button v-on:click="getData">Get Data</button>
<ul v-if="posts && posts.length">
<li v-for="(post, index) in posts" :key="`p-${index}`">
<p><strong>{{post.title}}</strong></p>
<p>{{post.body}}</p>
</li>
</ul>
<ul v-if="errors && errors.length">
<li v-for="(error, index) in errors" :key="`error-${index}`">
{{error.message}}
</li>
</ul>
</div>
</template>
<script>
import tiny from "tiny-json-http";
import axios from 'axios';
export default {
name: "LoaderTiny",
data() {
return {
msg: "Welcome to Your Vue.js App",
posts: [],
errors: []
};
},
methods: {
getData: function(event) {
const reqURL = `http://jsonplaceholder.typicode.com/posts`;
/*
this.posts = [
{
userId: 1,
id: 1,
title:
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
body:
"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
userId: 1,
id: 2,
title: "qui est esse",
body:
"est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
}
];
*/
/*
tiny.get( {reqURL} ).then(response => {
// JSON responses are automatically parsed.
console.log(response);
this.posts = response.body.Items;
}).catch(err => this.errors.push(err))
*/
try {
axios.get(reqURL)
.then(response => {
// JSON responses are automatically parsed.
this.posts = response.data;
})
.catch(e => this.errors.push(e));
} catch (err) {
console.log(err);
}
}
}
};
</script>
And in the Router (src/router/index.js):
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import LoaderTiny from '#/components/LoaderTiny'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/test',
component: LoaderTiny
},
{
path: '/',
component: HelloWorld
},
]
})
I am trying to set up a working workflow using the sass version of bootstrap. I have compass and sass all set up, but can't seem to get bootstrap working. This is what I have.
gruntfile
module.exports = function(grunt){
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-bootstrap');
grunt.initConfig({
uglify: {
my_target:{
files:{
'_/js/script.js' : ['_/components/js/*.js']
} // files
} // my_target
},// uglify
compass: {
dev: {
options:{
config: 'config.rb'
} // options
} // dev
}, // compass
bootstrap: {
dev: {
options:{
config: 'config.rb'
} // options
} // dev
}, // bootstrap
watch:{
options: {
livereload: true
}, // options
scripts:{
files: ['_/components/js/*.js'],
tasks: ['uglify']
}, // scripts
sass: {
files: ['_/components/sass/*.scss'],
tasks: ['compass:dev']
}, //sass
html:{
files: ['*.html']
} // html
} // watch
}) // initConfig
grunt.registerTask('default', 'watch'); // set to be the default task for grunt
} // exports
config.rb file:
require 'bootstrap-sass'
css_dir = '_/css'
sass_dir = '_/components/sass'
javascript_dir = '_/js'
output_style = :compressed
Here is my scss file:
#import "compass";
#import "bootstrap";
#import "_variables";
#import "_mixins";
#import "_base";
#import "_layout";
#import "_modules";
I'm not getting any errors but I don't see the bootstrap frame work moving my DOM elements around into rows and columns like I have them structured.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Roux Meetups</title>
<link rel="stylesheet" href="_/css/styles.css">
</head>
<body>
<h1>Hello</h1>
<div class="container">
<div class="row">
<div class="col-xs-6">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet saepe mollitia pariatur accusantium, facilis animi aperiam, culpa placeat nulla quaerat deleniti ipsa consequatur rerum minima modi. Nisi necessitatibus aut, fuga?</p>
</div>
</div>
</div>
<script scr="_/js/script.js"></script>
<script scr="http://localhost:35729/livereload.js"></script>
</body>
</html>
This is my first time trying to set this up, and I'm not sure what I am missing. My guess is that it might be the way I set up the task in the gruntfile. I was trying to replicate what I did for the compass task.
I figured out what I was missing. Inside the package.json file I needed to add the Bootstrap dependency. Here is my code for that:
{
"name" : "rouxmeetups",
"version" : "0.0.1",
"dependencies" : {
"grunt" : "~0.4.1",
"grunt-contrib-watch" : "~0.5.3",
"grunt-contrib-compass" : "~0.5.0",
"grunt-contrib-uglify" : "~0.2.2",
"matchdep" : "~0.1.2",
"bootstrap-sass-official": "3.2.0",
}
}