EDIT: REPL for answer https://svelte.dev/repl/eb7616fd162a4829b14a778c3d1627e4?version=3.48.0
What I'm talking would render this:
First we have a button:
<button on:click={add}> Add </button>
<div id="columnflexbox">
When I click it, this should happen:
<button on:click={add}> Add </button>
<!-- This exists in DOM-->
<div id="columnflexbox">
<!-- This div and components get rendered, the Div does not exist in DOM -->
<div style="display:flex;flex-direction:row">
comp1 (instance1)
comp2 (instance1)
</div>
When I click it again, another row gets added (with new instances): Add
<div id="columnflexbox">
<div style="display:flex;flex-direction:row">
comp1 (instance1)
comp2 (instance1)
</div>
<!-- Second div is generated, it does not exist in DOM, new instances of components -->
<div style="display:flex;flex-direction:row">
comp1 (instance2)
comp2 (instance2)
</div>
</div>
There is an interface:
export interface complexObject {
comp1 : ComplexObj1
comp2: ComplexObj2
}
let row: complexObject[] = []
Add function:
function add(){
let newObj:complexObject = {
comp1: new Comp1({target: div}), // I have to add a target here, or I get compile error, but how since the element doesn't exist?
comp2: new Comp2({target: div})
}
row.push(newObj);
}
I would use this way of doing it, but I can't since I'm getting compile error without adding target:
{#each row as rowitem }
<div >
{rowitem.comp1}
{rowitem.comp2}
</div>
{/each}
Edit: It turns out that the styling gets applied correctly when I add the target as the html specified in the render as so:
Add function:
function add(){
let div = document.createElement("div");
div.setAttribute('class',"row")
let newObj:complexObject = {
comp1: new Comp1({target: div}),
comp2: new Comp2({target: div})
}
row.push(newObj);
}
{#each row as rowitem }
<div >
{rowitem.comp1}
{rowitem.comp2}
</div>
{/each}
Now the problem is that the components do not get rendered, what I get instead is
[object Object] [object Object]
When I try to render as such:
<svelte:component this={rowitem.comp1}>
</svelte:component>
I get error:
TypeError: l is not a constructor
If I try to modify Add function and remove new
keyword I get this:
Type 'typeof comp1__SvelteComponent_' is missing the following properties from type 'comp1__SvelteComponent_': $$prop_def, $$events_def, $$slot_def, $on, and 5 more.ts(2740)
Turns out that this is an IDE problem and it does actually render correctly in the browser.
CodePudding user response:
For this, one would usually use svelte:component
which uses the constructor of the component as input and any props one may want to pass along. e.g.
<script>
import Comp from './Comp.svelte';
let components = [
[Comp, { content: 'Initial' }],
[Comp, { content: 'Initial 2' }],
];
function add(component, props) {
components = [...components, [component, props]];
}
</script>
<button type=button on:click={() => add(Comp, { content: 'Added' })}>
Add
</button>
{#each components as [component, props]}
<svelte:component this={component} {...props}>
(Slotted content)
</svelte:component>
{/each}
Example with types:
<script lang="ts">
import type { SvelteComponent, SvelteComponentTyped } from 'svelte';
import Comp from './Comp.svelte';
import Comp2 from './Comp2.svelte';
let components: [typeof SvelteComponent, Record<string, any>][] = [
[Comp, { content: 'Initial' }],
[Comp2, { color: 'blue' }],
];
function add<T extends typeof SvelteComponentTyped<P, any, any>, P>(
component: T,
props: P
) {
components = [...components, [component, props]];
}
</script>
add
enforces the consistency between the component and its props, the array cannot, at least when using tuples. If each item were a class, the class could enforce its internal type consistency.
CodePudding user response:
If you need a reference to a DOM element, you can either query it inside onMount
or set a reference to a variable via bind:this
(then as well defined/accessible inside onMount) REPL
<script>
import {onMount} from 'svelte'
import Comp from './Comp.svelte'
let container
onMount(() => {
const containerRef = document.getElementById('container')
console.log(containerRef, container)
const c = new Comp({target: container})
})
</script>
<div id="container" bind:this={container} />
(You query getElementByID
but your outer element has and do you iterate
newObjs
or row
? Because first is an object and can't be directly iterated I think...)