I have an index in npm package that exports vue components:
import HelloWorld from './HelloWorld/HelloWorld.vue'
import ByeWorld from './ByeWorld/ByeWorld.vue'
export {
HelloWorld,
ByeWorld
}
In the homepage project have a page with HelloWord import:
<template>
<div class="home">
<HelloWorld />
</div>
</template>
<script>
import { HelloWorld } from 'test-project-f/src/components';
export default {
name: 'home',
components: {
HelloWorld
}
}
</script>
Inspecting page with F12 Tools. We can see vue injecting both scss instead of only HelloWord. There isn't references of ByeWorld in whole project
<style type="text/css">.hello[data-v-541c6422] {
color: green;
}
</style>
<style type="text/css">.bye[data-v-ef3e2c3c] {
color: cadetblue;
}
</style>
Why? Anyone knows how to import only the necessary scss import?
I tried many ways:
import all in index
like a vue-cli bundle (vue-cli-service build --target lib --name
myLib ./src/components/index.js)
HelloWorld:
<template>
<div class="hello">
<p>Hello</p>
</div>
</template>
<script>
export default {
name: 'HelloWorld'
}
</script>
<style lang="scss" scoped>
.hello {
color: green;
}
</style>
ByeWorld:
<template>
<div class="bye">
<p>bye</p>
</div>
</template>
<script>
export default {
name: 'ByeWorld'
}
</script>
<style lang="scss" scoped>
.bye {
color: cadetblue;
}
</style>
Related
I am trying to style a p tag that is slotted into a child component using the tag.
Parent Code
<template>
<BasicButton content="Test 1234" #click="SendMessage('test')" height="10" width="50" />
<TransparentTopbar />
<BasicContainer width="90">
<p class="p-blueish-gray">{{ LorumIpsum() }}</p>
</BasicContainer>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import Homepage from "./components/home/Homepage.vue";
import TransparentTopbar from "./components/tools/topbar/TransparentTopbar.vue";
import BasicButton from "./components/tools/button/BasicButton.vue";
import BasicContainer from "./components/tools/container/basic_container/BasicContainer.vue"
//import func from "vue-temp/vue-editor-bridge";
export default defineComponent({
name: "App",
components: {
Homepage,
TransparentTopbar,
BasicButton,
BasicContainer
},
methods: {
SendMessage: sendMessage,
LorumIpsum: lorumIpsum
},
});
Child Code
<template>
<div class="light-rounded-container card-margins" :style="container_style">
<slot></slot>
</div>
</template>
<script lang="ts" src="./BasicContainer.ts" />
<style scoped src="./BasicContainer.css" />
import { defineComponent } from 'vue';
export default defineComponent({
name: 'BasicContainer',
props: {
width: {
type: Number,
default: 90,
required: false
}
},
data() {
return {
container_style: {
width: this.width.toString() + '%'
}
}
},
methods: {}
});
.light-rounded-container {
background-color: #242629;
border-radius: 15px;
align-self: center;
}
::v-slotted(p) {
color:red !important;
width: 1% !important;
padding-left: 5% !important;
padding-right: 5% !important;
padding-top: 25px !important;
padding-bottom: 15px !important;
}
The BasicContainer component is the one I am trying to style slotted content on. I would like to style the p tag that I pass to this component in the parent, but I would like to style it from within this child component.
#StevenB. you are corrent. I did not have my style in a separately defined NON-SCOPED .css file. After moving my styling into their and re structuring my HTML to avoid bleeding the css style into other global scope, it worked. Much appreciated.
I can nest components within the root App.vue component just fine but if I try and nest a component within a non root component nothing shows up. If I instead nest the Navbar component that wont show up in Splash.vue within App.vue it works, likewise if I move the Footer component to Splash.vue it doesn't.
App.vue (Footer component works fine, router then loads splash.vue)
<template>
<div id="app">
<Footer/>
<router-view/>
</div>
</template>
<script>
import Footer from '#/components/Footer'
export default {
name: 'App',
components: {
Footer
}
}
</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: 0px;
}
</style>
Splash.vue (Navbar component doesnt load, the text does load so I know the router is working correctly)
<template>
<div class="test">
<v-container fluid>
<Navbar/>
<p>splash loaded</p>
</v-container>
</div>
</template>
<script>
import Navbar from '#/components/layout/Navbar'
export default {
name: 'Splash',
data () {
return {
components: {
Navbar
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
.landing-card-style {
border-radius: 4px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14);
}
</style>
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
Vue.use(Vuetify)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
Navbar.vue
<template>
<div class="navbar">
<nav class = "deep-purple">
<div class="container">
<h1>navbar component loaded</h1>
</div>
</nav>
</div>
</template>
<script>
export default {
name: 'Navbar',
data(){
return{
}
}
}
</script>
<style>
</style>
You have your components inside data() function.
Try this instead:
export default {
name: 'Splash',
components: {
Navbar
}
}
I try to use the polymer app-layout but the menu slides behind my buttons and leaflet map. If i scroll down, it the content is still on top of the header. I tried to fix it with z-index but without any success. I use typscript and Polymer 3 in this project.
nav-bar.ts
import { PolymerElement, html } from "#polymer/polymer";
import "#polymer/iron-icons/iron-icons.js";
import "#polymer/app-layout/app-drawer-layout/app-drawer-layout.js";
import "#polymer/app-layout/app-drawer/app-drawer.js";
import "#polymer/app-layout/app-scroll-effects/app-scroll-effects.js";
import "#polymer/app-layout/app-header/app-header.js";
import "#polymer/app-layout/app-header-layout/app-header-layout.js";
import "#polymer/app-layout/app-toolbar/app-toolbar.js";
import "#vaadin/vaadin-button/vaadin-button.js";
import '#polymer/paper-icon-button/paper-icon-button.js';
import '#polymer/paper-checkbox/paper-checkbox.js';
import './my-app.js'
class NavBar extends PolymerElement {
static get template() {
return html`
<style>
app-header {
color: #fff;
background-color: #C62828;
--app-header-background-front-layer: {
background-position: 50% 10%;
};
}
[main-title] {
font-size: 2em;
}
</style>
<app-drawer-layout>
<app-drawer swipe-open slot="drawer">
<section>
<h2>app-header </h2>
<h2>app-header </h2>
</section>
</app-drawer>
<app-header-layout>
<app-header fixed="true" shadow="true" condenses="true"
slot="header">
<app-toolbar>
<paper-icon-button drawer-toggle>,
<iron-icon icon="search"></iron-icon></paper-icon-button>
<div condensed-title>Test</div>
</app-toolbar>
<app-toolbar></app-toolbar>
<app-toolbar>
<div main-title spacer>Test</div>
</app-toolbar>
</app-header>
</app-header-layout>
</app-drawer-layout>
<my-app appTitle="my app"></my-app>
`;
}}
customElements.define("nav-bar", NavBar);
Even if I copy past one of their demos from: webcomponents.org app-layout its the same problem. I also tried using this in my index.html but with the same result:
<nav-bar></nav-bar>
<my-app></my-app>
This is how it looks:
scrolling the button "Test" is from my-app and not in the header
with the menu open another example with leaflet map and buttons
Is there any way I can change this behaviour and always have the header and the menu as the first layer?
I think if you separate toolbar with drawer, it will work as you wish.
Here a working example: DEMO
import { PolymerElement } from "#polymer/polymer";
import {html} from '#polymer/polymer/lib/utils/html-tag.js';
import "#polymer/iron-icon/iron-icon.js";
import '#polymer/iron-icons/iron-icons.js';
import "#polymer/app-layout/app-drawer-layout/app-drawer-layout.js";
import "#polymer/app-layout/app-drawer/app-drawer.js";
import "#polymer/app-layout/app-scroll-effects/app-scroll-effects.js";
import "#polymer/app-layout/app-header/app-header.js";
import "#polymer/app-layout/app-header-layout/app-header-layout.js";
import "#polymer/app-layout/app-toolbar/app-toolbar.js";
import "#vaadin/vaadin-button/vaadin-button.js";
import '#polymer/paper-icon-button/paper-icon-button.js';
import '#polymer/paper-checkbox/paper-checkbox.js';
import './my-app.js';
class NavBar extends PolymerElement {
static get properties() { return {
drawerOpened:{type:Boolean}
}}
static get template() {
return html`
<style>
host:{
display:block;
background-color:red;
}
app-header {
color: '#fff';
background-color: grey;
}
[main-title] {
font-size: 2em;
}
app-toolbar.tb1 { background-color: yellow;}
app-toolbar.tb2 { background-color: green;}
app-toolbar.tb3 { background-color: brown;}
paper-icon-button { color: white }
app-drawer > * { padding: 20px;}
</style>
<app-header fixed="true" shadow="true" condenses="true"
slot="header">
<app-toolbar spacer class='tb1'>
<paper-icon-button icon='menu' on-tap='drawerToggle'></paper-icon-button>
<div condensed-title> Tool Bar-1</div>
</app-toolbar>
<app-toolbar class='tb2'></app-toolbar>
<app-toolbar class='tb3'>
<div main-title spacer>Main Title</div>
</app-toolbar>
</app-header>
<app-drawer-layout>
<app-drawer opened="{{drawerOpened}}" swipe-open slot="drawer">
<h1> Side Menu</h1>
<h2> App-header </h2>
<h2> App-header </h2>
</app-drawer>
</app-drawer-layout>
<my-app appTitle="my app"></my-app>
`;}
drawerToggle(){
this.drawerOpened = !this.drawerOpened;
}
}
customElements.define("nav-bar", NavBar);
Some components cannot be injected into the App.vue template. Particularly, the Login.vue is the one that is causing issues. If I create it from scratch while the server is running (npm run dev) it will work, but once I restart it and navigate to the Login component, it will fail to load.
Stack Trace
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Login>
<App> at src/App.vue
<Root>
src/components/Login.vue
<template>
<div id="login">
<ul>
<li><input type="text" v-model="username" placeholder="Username"></li>
<li><input type="password" v-model="password" placeholder="Password"></li>
<li><button #click="login(username, password)">Login</button></li>
</ul>
</div>
</template>
<script>
</script>
<style scoped>
#login{
padding-top: 100px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
</style>
src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '#/components/Hello'
import RideSharing from '#/components/RideSharing'
import Login from '#/components/Login'
import About from '#/components/About'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello
},
{
path: '/app',
name: 'RideSharing',
component: RideSharing
},
{
path: '/app/login',
name: 'Login',
component: Login
},
{
path: '/app/about',
name: 'About',
component: About
}
]
})
src/App.vue
<template>
<div id="app" :style="{padding: $route.path === '/' ? '172px' : '10px'}">
<div>
<img src="./assets/logo4.png" align="center">
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
background-color: hsla(269, 96%, 50%, 0.73);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 0px;
height: inherit;
}
body{
height: inherit;
}
html{
height: 100%;
}
</style>
src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import BootstrapVue from "bootstrap-vue"
import App from './App'
import router from './router'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
If you are using the full build version of Vue then you could try to mount the DOM template using .$mount('#app')
So, in your main.js, your app script would look like this:
new Vue({
router,
template: '<App/>',
components: { App },
}).$mount('#app')
SOLUTION:
Missing .vue extensions in imports.
src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '#/components/Hello.vue'
import RideSharing from '#/components/RideSharing.vue'
import Login from '#/components/Login.vue'
import About from '#/components/About.vue'
Hopefully this has been answered before - essentially I'm trying to append a block ("CodeBlock.vue") to an element inside App.vue from an onClick event triggered inside a sibling of CodeBlock, and a child of App.vue, ("ButtonSidebar.vue"). I'm a little confused by emitting events and/or using an eventBus Vue instance, so any pointers would be greatly appreciated:
So far I have the following. CodeBlock.vue which will be used as an instance and appended to a div inside App.vue.
CodeBlock.vue:
<template>
<div :class="type">
THIS IS A CODE BLOCK!!
</div>
</template>
<script>
export default {
name: 'CodeBlock',
props: [ 'type' ]
}
</script>
App.vue:
<template>
<div id="app" class="container">
<ButtonSidebar/>
<div id="pageBlocks" ref="container"></div>
</div>
</template>
<script>
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
// import { eventBus } from './main'
import AddTitle from './components/modules/AddTitle'
import AddSubTitle from './components/modules/AddSubTitle'
import ButtonSidebar from './components/modules/ButtonSidebar'
import CodeBlock from './components/modules/CodeBlock'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
export default {
name: 'App',
components: {
AddTitle,
AddSubTitle,
ButtonSidebar,
CodeBlock
}
}
</script>
<style>
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #1f1f1f;
margin-top: 60px;
}
.no-border {
border: unset !important;
border: 0px !important;
}
</style>
ButtonSidebar.vue:
<template>
<div>
<b-button class="btn-circle absolute-float-tight text-dark" v-on:click="reveal=!reveal">
<font-awesome-icon v-if="!reveal" :icon="faPlusIcon" />
<font-awesome-icon v-if="reveal" :icon="faMinusIcon" />
</b-button>
<transition name="custom-classes-transition" enter-active-class="animated bounceInDown" leave-active-class="animated bounceOutRight">
<div v-if="reveal" class="absolute-float-reveal">
<b-button class="btn-circle text-dark" v-on:click="addCodeBlock"><font-awesome-icon :icon="faCodeIcon" /></b-button>
</div>
</transition>
</div>
</template>
<script>
import Vue from 'vue'
import FontAwesomeIcon from '#fortawesome/vue-fontawesome'
import faPlus from '#fortawesome/fontawesome-pro-regular/faPlus'
import faMinus from '#fortawesome/fontawesome-pro-regular/faMinus'
import faCode from '#fortawesome/fontawesome-pro-regular/faCode'
import CodeBlock from './CodeBlock'
export default {
name: 'ButtonSidebar',
computed: {
faPlusIcon () {
return faPlus
},
faMinusIcon () {
return faMinus
},
faCodeIcon () {
return faCode
}
},
components: {
FontAwesomeIcon,
CodeBlock
},
data () {
return {
reveal: false
}
},
props: ['codeBlocks'],
methods: {
addCodeBlock () {
var ComponentClass = Vue.extend(CodeBlock)
var instance = new ComponentClass({
propsData: { type: 'primary' }
})
instance.$mount()
this.$el.querySelector('#pageBlocks').appendChild(instance.$el)
}
}
}
</script>
<style scoped>
.absolute-float-tight {
left: 20px;
position: absolute;
}
.absolute-float-reveal {
left: 60px;
position: absolute;
}
.btn-circle {
background-color: transparent;
border-radius: 50%;
height: 34px;
padding: 0;
width: 34px;
}
</style>
It's around the this.$el.querySelector('#pageBlocks').appendChild(instance.$el) part that I start to loose the plot a bit...I'm worried that I have to strip everything down and start again perhaps?
You should avoid reaching to the DOM as much as possible. The source of truth for the data should be in your components.
refs are very useful to integrate other js library that needs a DOM element.
So in your case, assuming codeBlocks are available in your App.vue components, the SidebarButton needs to emit an event when it's clicked so that the parent App.vue can add a new Codeblock:
(I have removed some code not needed for the example. CodeBlock.vue stays the same)
App.vue
<template>
<div id="app" class="container">
<ButtonSidebar #add-block="addCodeBlock" />
<CodeBlock v-for="block in codeBlocks" :type="block.type" />
</div>
</template>
<script>
import ButtonSidebar from '../ButtonSidebar'
import CodeBlock from '../CodeBlock'
export default {
name: 'App',
components: {ButtonSidebar, CodeBlock},
data() {
return {
codeBlocks: []
}
},
methods: {
addCodeBlock() {
const newBlock = {type: 'whatever'}
this.codeBlocks.push(newBlock)
}
}
}
</script>
ButtonSideBar.vue
<template>
<div>
<b-button class="btn-circle text-dark" v-on:click="addCodeBlock</b-button>
</div>
</template>
<script>
export default {
name: 'ButtonSidebar',
data () {
return {
reveal: false
}
},
methods: {
addCodeBlock () {
this.$emit('add-block')
}
}
}
</script>
A good pattern to follow in Vue is to lift the state to the parents and passing it down as props whenever you feel like you want to share state between parents and children.
I think you could achieve it in this simple way:
App.vue (template section)
<ButtonSidebar #add="addCodeItem"/>
<div id="pageBlocks">
<codeBlock v-for="code in arrCodes" :type="code.type"/>
</div>
App.vue (script)
export default {
data() {
return {
arrCodes: []
}
},
methods: {
addCodeItem(codeType) {
this.arrCodes.push( { type: codeType } )
}
}
}
ButtonSidebar.vue (script section)
addCodeBlock () {
this.$emit('add', 'yourtype');
}