vuejs2 dynamic css with dynamic html - javascript

A plugin I use creates dynamic html and I want to add a dynamic background-color using a hex passed via props.
This is the html in my component
<template>
<div class="pagination" slot="pagination"></div>
</template>
Generates dynamic HTML of this
<div class="pagination" slot="pagination">
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
</div>
The components receives props
props: ['primaryBgColor']
I can obviously see the color in the template if I write
<template>
<div>
{{ this.primaryBgColor }}
</div>
</template>
However when I write a style within the component like
<style>
.swiper-pagination-bullet {
background-color: {{ this.primaryBgColor }}
}
</style>
Webpack returns an error saying I have a CSS syntax error. Any ideas?

suresh's answer may not work as it listens to the Vue mounted event, but by the time Vue component mounted, the plugin element may not yet be been injected.
if the plugin provides a similar event, you can register the handler there. if not, you can listen to the outer dom element using MutationObserver instead.
<template>
<div id="outer">
</div>
</template>
<script>
const observer = new MutationObserver(mutations => {
// suresh's function here
});
observer.observe(document.getElementById('outer'), { childList: true });
</script>

In your template, you can directly inject style
<template>
<div :style="this.primaryBgColor">
{{ this.primaryBgColor }}
</div>
</template>
primaryBgColor should contain object like {'background':"#cccc"}
For more option, vuejs had superb documentation. you can refer https://v2.vuejs.org/v2/guide/class-and-style.html#Object-Syntax-1
We can query the element and apply style like as follows
mounted: function () {
var elems = document.querySelectorAll('.swiper-pagination-bullet ')
var index = 0
var length = elems.length
for (; index < length; index++) {
elems[index].style.backgroundColor = this.primaryBgColor
}
},

Related

How to add conditional styling in Vue.js using a method?

I want to add conditional styling in a child component based on the values of a prop passed from the parent component.
This a working example of conditional styling:
<li v-bind:class="[booleanValue ? 'stylingClassOne' : 'stylingClassTwo']"
but this is only applicable for when my styling is based on a single variable which can only be of two values (true/false).
I want to achieve conditional styling based on a variable that can take multiple values. Assume I pass a string from my parent component to my child component stylingDecider, which can be of values stylingClassOne, stylingClassTwo, stylingClassThree.
Therefore I want to do the following:
<li v-bind:class="getStylingClass(stylingDecider)"> but this does not work. The reason I need a method to decide what the styling is because there will be some other processing going on in the that will return a class based on said processing, so I can't just use <li v-bind:class="stylingDecider".
What am I doing wrong? Please advise, thanks.
I am using Vue 3 and bootstrap-vue 3.
I just created a working code snippet:
Vue.component('child', {
props: ['dynamicstyle'],
template: `<ul><li v-bind:class="getStylingClass(dynamicstyle)">Hello !!</li></ul>`,
methods: {
getStylingClass(stylingDecider) {
return stylingDecider;
}
}
});
var app = new Vue({
el: '#app',
data: {
stylingDecider: 'stylingClassTwo'
}
});
.stylingClassTwo {
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child :dynamicstyle="stylingDecider">
</child>
</div>

Is it bad practice to create a web-component inside another and append it to said other?

Essentially I have a web component "x" and I dynamically create a form component inside the "x" which will be appended to "x".
I could just do it in the place I create "x", after creating "x", of course.
Basically this:
class X extends LitElement {
render() {
return html`
<div>
<slot name="form-component">${this.appendFormComponent()}</slot>
</div>
<slot></slot>
`
}
appendFormComponent() {
const formComponent = document.createElement('input')
formComponent.slot = "form-component"
this.append(formComponent)
}
// side note, is running this append inside the render function a terrible
// idea and where should I do it instead? I mean doing it in the render
// function does appear to work...
}
As you suspected, this is definitely a terrible idea because you are mixing imperative paradigm with declarative paradigm. However, if you really need to do this and since you are using LitElement, you can nicely abstract the declarative and imperative UI code using appropriate lifecycle methods:
class X extends LitElement {
render() {
return html`
<div>
<slot name='form-component'></slot>
</div>
<slot></slot>
`;
}
// Executed only once
firstUpdated() {
const formComponent = document.createElement('input');
formComponent.slot = 'form-component';
this.append(formComponent);
}
}
Also, the approach you are attempting is probably problematic. Your problem would be easily solved by render function only:
class X extends LitElement {
render() {
return html`
<div>
<slot name='form-component'>
<!-- Notice the use of INPUT TAG here -->
<input type='text' />
</slot>
</div>
<slot></slot>
`;
}
}
Using something like firstUpdated with document.createElement should be used to create UI components which have offset elements that break the UI as Function of State notion. Such components are date pickers, multi select dropdown, dialog boxes, etc. which directly append DOM elements to the body for managing Z-index and fixed positioning accurately.
Further, as per your comments, if you have a dynamic function which needs to be assigned to the input text, simply create a wrapper function like:
class X extends LitElement {
// Input change event handler
onChange() {
// A guard to check presence of dynamic function
if (this.someDynamicFuction) {
this.someDynamicFuction();
}
}
render() {
return html`
<div>
<slot name='form-component'>
<!-- Notice the use of INPUT TAG here -->
<input type='text' #change=${this.onChange} />
</slot>
</div>
<slot></slot>
`;
}
}

Vue.js - inject a v-on button to spawn an overlay from static html sourced from an external source

Using Axios, I'm pulling in a static HTML file. This part is working
The user clicks on an edit button and I'm going through that static HTML and adding a new class if an existing class exists.
If that existing class exists, I want to add a new button with v-on in this static HTML template and re-render the content with this new button in the HTML which then spawns an overlay.
Is there anyway that I can add this new button in my code so that view re-renders and uses the Vue v-on directive?
Here is my code:
VIEW:
<template>
<div>
<div class="row">
<div id="kbViewer">
<b-button
class="request-edit"
#click="letsEditThisStuff({currentUrl: currentUrl})">Request An Edit</b-button>
<div v-html="htmlData">
{{ htmlData }}
</div>
</div>
</div>
</div>
</template>
data: function () {
return {
sampleElement: '<button v-on="click: test()">test from sample element</button>',
htmlData: '',
};
},
methods: {
pullView: function (html) {
this.axios.get('../someurl/' + html).then(response => {
let corsHTML = response.data;
let htmlDoc = (new DOMParser()).parseFromString(corsHTML, "text/html");
this.rawDog = htmlDoc;
this.htmlData = htmlDoc.documentElement.getElementsByTagName('body')[0].innerHTML;
})
},
letsEditThisStuff(item) {
let htmlDoDa = this.htmlData;
// This doesn't work - I'm trying to loop over the code and find all
// of the class that are .editable and then add a class name of 'editing'
// to that new class. It works with #document of course...
for (const element of this.htmlData.querySelectorAll('.editable')) {
element.classList.add('editing');
// Now what I want to do here is add that sampleElement from above - or however -
// to this htmlData and then re-render it.
let textnode = document.createElement(sampleElement);
textnode.classList.add('request-the-edit')
textnode.innerHTML = 'edit me!'
element.append('<button v-on="click: test()">test from sample element</button>')
console.log('what is the element?', element)
}
this.htmlData = htmlDoDa
},
}
I know that some of my variables are not defined above - I'm only looking at a solution that helps with this - basically take that stored data.htmlData, parse through it - find the classes with "editable" and append a button with a v-for directive to that specific node with "editable" ... Unfortunately, the HTML already exists and now I've got to find a slick way to re-parse that HTML and re-append it to the Vue template.
I found Vue Runtime Template and it works PERFECTLY!
https://alligator.io/vuejs/v-runtime-template/

Execute function when clicking on DOM element in Vue.js

I want to execute a function when I'm clicking on elements in the dom with a specific class. It just doesn't work, but I'm also receiving any error. This is my
code snippet:
methods: {
initTab: function(){
document.querySelectorAll('.element').onclick = this.nextTab()
}
},
mounted: function () {
this.initTab()
}
I
I want to execute the function every time I click on the element. Would be very thankful if anybody could help me :)
There's very little need (if at all) for document.querySelectorAll() in a Vue app.
In this situation you can take advantage of delegation:
<div #click="onClick">
<!-- Clicks on any element inside this div will be handled -->
</div>
methods: {
onClick(e) {
if (e.target.classList.contains('element')) {
// Handle the click
}
}
}
Add #click="initTab($event)" to the document or template root, that allows you to track every click event on your template, that way you could put your logic to the elements which have only .element class name. If you're using it in a component you could do : <template> <div #click="initTab($event)"> ... </div> </template>
var app = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
nextTab(){
console.log("You clicked on an element with class name =element")
},
initTab(event){
let targetClassNames=event.target.className.split(" ");
targetClassNames.filter(e=>{
if(e==="element"){
this.nextTab();
}
});
}
},
mounted() {
}
})
#app{
height:100px;
display:grid
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app" #click="initTab($event)">
<button class="element">1</button>
<button class="element btn">2</button>
<button class="btn">3</button>
<button class="element btn-primary">4</button>
<button class="btn elementory">5</button>
</div>
You're trying to use general javascript logic within vue. This is not often a good idea.
What I do in such cases is something like this:
<component-name #click="nextTab(tabName)"></component-name>
However, in a v-for loop you can also do something like this:
<ul v-for="tab in tabs">
<li #click="nextTab(tab)">{{tab}}</li>
</ul>
That way in methods you only need:
methods: {
nextTab: function(tab){
// whatever it is you want to do here
}
},
And you won't need mounted at all.
Conclusion: try to avoid repetition by creating components or elements (like li) that repeat - not by trying to add an event-listener to a class.

Libray wrapped React component, how to find DOM children with Enzyme?

I am trying to test a library wrapper component, which generates it's own markup rendered in componentDidMount. Given the following...
// <MyComponent />
componentDidMount() {
transform(this.ref);
}
render() {
return (
<div className='foo' ref={(self) => this.ref = self} />
)
}
where (external lib) transform does something to alter the rendered markup. Assume this to be transformed to the following...
<div class="foo">
<article>
<h2>noms</h2>
<section>
<ul class="list">
<li>pizza</li>
<li>taco</li>
</ul>
</section>
</article>
</div>
How do I actually use the Enzyme API on the rendered markup?
I am trying to mount the component, then to find my .list element, but the result is never actually found with a length of 0. What is wrong with my following test?
let wrapper = Enzyme.mount(<MyComponent />);
let list = wrapper.find('.list'); // nope
I believe my basic setup to be correct, as calling wrapper.html() does actually return the above transformed markup in full. What am I missing here?
Since wrapper is your component, and ref is a property of your component that points to the DIV, this should work:
let wrapper = Enzyme.mount(<MyComponent />);
let list = wrapper.instance().ref;

Categories

Resources