How to work with vue.js and window.getSelection()? - vue.js

Here is an example from some tutorial:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.setStart(p.firstChild, 2);
range.setEnd(p.querySelector('b').firstChild, 3);
alert(range); // ample: italic and bol
// use this range for selection (explained later)
window.getSelection().addRange(range);
</script>
How to do this with vue?
Should I use query selectors too?
I am interested how to do this selection manipulation within contenteditable. I can use "ref" for contenteditable container but inner content with bold, italic etc. tags is dynamic and mark this code with refs:
... <b><i>some text</i></b> ...
isn't appropriate.

If you're using the Selection API to manipulate the DOM (not just creating ranges, but adding/removing/modifing/reordering DOM nodes), then you should be doing those manipulations on DOM elements not managed by Vue. You must not modify parts of the DOM managed by Vue because Vue won't know about those changes and it will get confused when trying to patch the DOM during the next render cycle. Vue "owns" the rendered DOM of its components.
This doesn't mean you can't use the Selection API and Vue together, it just means you need to be careful not to tinker in the DOM willy-nilly.
Should I use query selectors too? Using p.querySelector('b').firstChild isn't vue way but I don't know what should I use insted
This rule only applies if you want to get a reference to a DOM node rendered by Vue. If the container were content editable, then its contents would not be managed by Vue and you would have no way of referencing its contents without using querySelector and other DOM functions. Try not to get caught up with "am I doing it the Vue-way?" when what you are trying to do is inherently not Vue related anyway.

Related

Render non-vuejs HTML in a component

I have a working VueJS app with nested components.
In one of the components, I want to be able to inject arbitrary HTML but I don't want VUEJS to parse it : I want plain old HTML tags (scripts, iframes, style, divs, native events, whatever I need).
I know I can do that outside the root "#app", but is there a way to do that inside it ?
Thanks.
You can use v-html directive, it takes an expression that evaluates to a string and sets the element's innerHTML to that string. Vue won't parse it as a template and insert it into the DOM instead. This is one of those things that you need to pay attention to security for.
See docs: https://vuejs.org/api/built-in-directives.html#v-html

Vue3 "v-is" component with varying element tag

The Vue3 documentation explains how to create a component that replaces e.g. a "tr" node, see DOM template parsing caveats.
The render function isn't shown, but I assume that it creates VDOM with a "tr" as top element.
In vue2 I could write such a component for varying elements, by accessing the data of the node that my component was supposed to replace. In the render function I could access the information of the node with the "v-is" as this.$vnode.data, especially the tag as this.$vnode.data.tag. So my component could adapt to the element that it was used for by creating a result with h(this.$vnode.data.tag, ...).
This information (this.$vnode.data) does not seem to be available anymore in vue3. I looked at the render function's "this" and the global API for functions available in "render". No luck. Does anybody know how I can obtain this information in vue3?

Access dom element with vuejs and vuetify in v-tabs

I have a div tag with an 'id="meet"' into v-tabs.
I need to access the node of this tag after I click a tab. I am using
let node=document.querySelector("#meet").
My problem is that it always returns "null". Here is the codepen: https://codepen.io/luizalves/pen/WNrepxz
What is wrong here?
There is no guarantee that after $nextTick you will see DOM element immediately.
<div id="meet"></div>
<button #click="onTest">test</button>
...
methods: {
onTest () {
let node=document.querySelector("#meet");
console.log('meet here',node)
},
After you clicked test you'll see in a console:
"meet here" "<div id='meet'></div>"
You can try to extract this div with an unique id into a separate component and inside it you can use mounted hook to know that it exists in DOM
Of course, according to vuetify docs, you may add eager prop to v-tab-item component, and that's it, but...possibly you are doing something wrong.
It's not a good idea to always add this prop because you are losing one of the vuetify 2.X advantages - lazy loading. This may lead to big performance problems if there are many elements on the tab.
Maybe it would be better if you will work directly with reactive variables (like variables in v-model directive), not DOM objects.

Target and manipulate single DOM element in vue

Somehow I still can't wrap my head around some core vue concepts.
I have made some simple webpage using phalcon. Created it so, that it would work without JS and now is the time to add some bells and whistles - ajax queries and the like, for the user experience to be better.
I wanted to do everything using vue, to see how it all adds up. But after hours of googling I still can't find solution for the simplest of tasks.
Say: I want to get a text paragraph in a series of <li>-s and change it somewhat. Maybe make excerpt of it and add 'see more' button behind it. Now, in jQuery I would just iterate with each() and perform the tasks. With vue targeting set of DOM elements is much harder for me, probably because of whole paradigm being "the other way round".
I know I could iterate with v-for, but these elements are already in the DOM, taken from the database and templated with volt. I had even this wild idea of creating .js files from phalcon, but it would completely negate my strategy of making functional webpage first and then enhance it progressively.
Frankly speaking I feel like I'm overcomplicating for the sake of it, right now. Is vue even fit for a project like this, or is it exclusively a tool to build app from the ground up?
Vue's templating is client-side, which means if you are delivering an already templated html page (by your backend) there is little vue can still do for you. Vue needs data, not DOM elements to build its viewmodels.
This becomes pretty obvious when building a single page application for example, which would be rendered only on the client-side. You'd simply load the data asynchronously from a backend api (REST for example) and then do all the rendering on the client.
As far as I understand your usecase you want to mix client and server side rendering, rendering most of the non-interactable content using your backend's templating engine and adding some interactivity using vue. In this case you'll need to add some vue components (with their own rendering logic) to your backend template and pass data to that component using vue's data-binding.
Here's an example:
...
<div id="app">
<my-vue-list :products="{% products %}"></my-vue-list>
</div>
...
And in your JS:
var app = new Vue({
el: '#app',
data: {
components: {MyVueList} // You will have to register all the components you want to use here
}
})
Vue provides the ref attribute for registering a reference to a dom element or child component:
// accessible via this.$refs.foo
<li ref="foo">...</li>
Do note, however, that refs are not reactive, as stated in the docs:
$refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

Initialize dynamic Component in Code using Vue.js

I am currently developing a web application that is used to display elements for events on a map provided by HERE Maps. I am using Vue.
I have some components, but the relevant component is the component HereMaps.vue which initializes the map using the HERE Maps Api.
The HERE Maps Api provides the possibility to place so called InfoBubbles on the map showing additional information. These InfoBubbles can be provided some HTML-code in order to customize their appearance.
Please refer to the documentation for additional information
Following the documentation the code looks something like this:
let bubble = new H.ui.InfoBubble(marker.getPosition(), {
content: "<div class='someClass'>Some Content</div>"
});
this.ui.addBubble(bubble)
This is happening after mount in the "mounted" method from Vue in the "HereMaps" component.
The Bubbles are added in a "closed" (hidden) form and dynamically "opened" to reveal their content when the corresponding marker icon on the map is clicked. Therefore the HTML-code is present on the DOM after the component is mounted and is not removed at a later stage.
Now instead of supplying custom code within each bubble added to the UI i want to just add a component like this:
let bubble = new H.ui.InfoBubble(marker.getPosition(), {
content: "<myDynamicComponent></myDynamicComponent>"
});
this.ui.addBubble(bubble)
It does not matter to me wether the component is initialized using props or if it is conditionally rendered depending on the state of a global variable. I just want to be able to use the "myDynamicComponent" in order to customize the appearance in a different file. Otherwise the design process gets very messy.
As far as i know this is not possible or at least i was not able to get it work. This is probably due to the fact that the "myDynamicComponent" is not used within the "template" of the "HereMaps" component und thus Vue does not know that it needs to render something here after the directive is added to the DOM in the "mounted" method.
This is what the InfoBubble looks using normal HTML as an argument:
This is what the InfoBubble looks using the component as an argument:
It appears to just be empty. No content of the "myDynamicComponent" is shown.
Does anyone have any idea how i could solve this problem.
Thank You.
Answer is a bit complicated and I bet you wouldn't like it:)
content param can accept String or Node value. So you can make new Vue with rendered your component and pass root element as content param.
BTW, Vue does not work as you think, <myDynamicComponent></myDynamicComponent> bindings, etc exists in HTML only in compile time. After that all custom elements(components) are compiled to render functions. So you can't use your components in that way.
Give us fiddle with your problem, so we can provide working example:)