I need the bounding client rect of an anchor element.
I mark that using bind:this={someVariable}
But when I add that code to a svelte component, I get some other object svelte uses to represent a component.
can I access the enclosed element or something like that from that binding?
Or do I have to wrap every component with a sacrificial <div /> and bind that?
Using bind:this does not give you a DOM Element because Svelte does not require that your component has a root element. This is a valid Svelte element:
<script>
...
</script>
<div>...</div>
<div>...</div>
And so is a component that does not have any markup at all:
<script>
...
</script>
In both those cases, it would be impossible to return a bounding client rect for these components because there is no 'root' element.
That said, what you can do is add a bind:this in your component and export that one:
<script>
export let anchor
</script>
<svelte:options accessors></svelte:options>
<a bind:this={anchor}>...</a>
And in your parent you can now get the anchor element using child.anchor (note that by default the props are not accessible this way, that's why you need the svelte:options)
One of the reason this is not so straightforward though is that it is a very 'unsvelte' way of working, you would normally not direcly interact with the markup generated by another component (that is the domain of that component).
Alternatives could be
expose the client rectangle directly instead of the element
move what you are trying to do to the anchor component itself
Related
I have two separate custom element components and if I want to do something with the ComponentB's element inside of ComponentA, what is the best way to do it?
index.html
<body>
<componentA>
<div class="container">
<p>I am component A</p>
<p>abc</p>
</div>
</componentA>
<componentB>
<div class="container">
<p>I am component B</p>
<p></p> // Will set data between the p tag in componentA
</div>
</componentB>
</body>
componentA.js
...component template...
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
connectedCallback() {
this.setInnerTextToComponentBElement();
}
setInnerTextToComponentBElement(){
// How to set componentB's second p tag innerText?
}
}
customElements.define('componentA', ComponentA );
I have thought of using document.querySelector to get the componentB element and go from there... but is this the best practice way to do it?
I would suggest NEVER tying two components together without allowing the developer using the components to provide a way to connect them.
Normally communications or interconnections between elements is handled by the parent. The only element that I know of that does this by itself is the <label> element. If this is the parent of an <input>, <select>, <button> or <textarea> then it will pass its focus on to the child element.
But to use it with a sibling element you have to set the for attribute of the <label> to be the id of the other field. And then the other field needs to have its id set.
I answered something like this here:
How to communicate between Web Components (native UI)?
Once you tie two components together these components are no longer usable by themselves unless you write extra code to allow separation.
Instead, either allow the components to dispatch events that the parent will receive and the parent will then pass values on to the other component. Or, follow the example of how <label for=""> works.
If i have a presentational (child) component with the following line:
<small id="parameters"> some parameters text </small>
I noticed that if i go to its parent container component and try to get that element by:
const textElem = document.getElementById('parameters').value;
It is not going to get its value.
Why is that? If i use document in a component, is it only "local" to that component?
You can use document inside a component, to reference the global document object, it's not local to that component, however, at the point you're referencing document.getElementById('parameters') this element might not have been rendered yet to the dom, so make sure to call it after the child element has rendered.
On the other hand, maybe you meant to use: document.getElementById('parameters').innerHTML
to get the text inside that element, instead of .value
I have a container who have inside many elements like h1, p etc.
This container have a background, and I want create method to move him by mouse coordinates. I have a problem with access to this element, because e.target show me elements inside container, not container who is binded element.
Alive any solution to share all functions between main component and child components? Because when i want get this method in anyone place now i must add :moveHero="moveHero" to every component and get this in prop array, so i want create this more globally in one place and share this to all childs. Now i think one solution for this is create .js file and import that to Vue.
You can add $ref to the container element like this: <div ref="container-ref">.
Then to access the container element in vue methods, just do this.$refs["container-ref"].
Explanation:
You can add ref attribute to any tag, even child components. It is parsed by vue only, browser ignores it.
If the ref is attached to a HTML tag, this.$refs[key] will return the corresponding DOM element.
If the ref is attached to a child component, this.$refs[key] will return the corresponding vue instance.
I'm trying to create some sort of parent component that gives me some general appearance and logic for all my components, but in which I can write some specific logic and templating inside.
Look at these images:
Both of these components have common traits: an upper part with a title and a "hide/show" button, and then, a "body" that differs between both of them. They also share that edit button and so, but my idea is to make that "body" part completely dynamic: that's the part of the component that I want to override with my implementation components.
In other words: I want to make an abstract class with Typescript that holds the template: the rectangle, the header and body, the title (which I want to override from the subclass)... and then, the "body" will be filled with the template provided by the subclass.
How can this be achieved in Angular? This far I've only seen ways of overriding completely the parent components templates, not to complement one with the other. I guess there must be a way to insert HTML code with Angular directives inside a component...
Thing is, which is it?
Thank you!
Let's assume you named your component CardComponent and your selector is app-card.
For the title
You can simply use the #Input() component decorator to get the title string and use it in your generic CardComponent, see the angular documentation: https://angular.io/guide/component-interaction
For the body:
You can create your component containing the header and Edit button and then use the <ng-content> tag to get what's inside the component selector, for example:
<h2>{{title}}</h2>
<ng-content></ng-content>
<button>Edit</button>
And so you can use your card component like this:
<app-card [title]="Channel select">
<!-- Insert your body html -->
</app-card>
PS: to learn more about ng-content, see this: https://medium.com/claritydesignsystem/ng-content-the-hidden-docs-96a29d70d11b
How to target method inside other component?
I'm working on a project where I want to target a method inside another component. I press a button inside the dashboard component and I want to change the data inside my line-chart component (which uses vue-chartjs). How can I target this method?
Well you could target it over $refs however, this is pretty dirty, as it prodvides a pretty strict binding of your componentes.
A better solution would be to trigger an event (eventbus) or over an prop.
You can call children's method by referencing them through refs
In your example, your dashboard's template should looks like this :
<template>
<div>
<button #click="$refs.chart.yourMethod()">Call child method</button>
<line-chart ref="chart"></line-chart>
</div>
</template>