Yes, you can't do this in Svelte.
You'll have to figure out how you can achieve the same outcome with the APIs available in Svelte.
For example, you can use :<slot>
<TabPane
name="profile"
key="1"
>
<img slot="title" src="images/profile.svg" alt="Profile" />
<!-- some code, eg: -->
<div>Some code here</div>
</TabPane>
<!-- filename: TabPane.svelte -->
<h1>
<slot name="title" />
</h1>
<slot />
The element with attribute will be inserted into slot="title"<slot name="title">
and the rest of the elements will be inserted into <slot />
This is equivalent in React:
function TabPane({ title, children }) {
return (
<>
<h1>{title}</h1>
{children}
</>
);
}
If you want to pass in only string for the title, you could wrap the string around with so that you can add the <svelte:fragment> attributeslot="title"
<TabPane
name="profile"
key="1"
>
<svelte:fragment slot="title">
string title here
<svelte:fragment>
<!-- some code, eg: -->
<div>Some code here</div>
</TabPane>
Reference:
This is wrong:
<Button buttonProps={...button_props} />
If a named property is assigned, there should not be a spread:
<Button buttonProps={button_props} />
Spreading is for assigning many individual properties where the keys on the spread object correspond to the properties. E.g.
<script>
// ...
const stuff = { a: 1, b: 2 }; // Name of object does not matter
</script>
<Button {...stuff} />
<!-- Button -->
<script>
export let a;
export let b;
</script>
Svelte ships with stores, a pretty neat way to manage global state; with these, you don't have to pass references between components. Here's a simplified solution using a writable store:
store.js
import { writable } from 'svelte/store';
// An observable string value.
export const focused = writable('');
searchbar.svelte
<script>
import { focused } from './store'
export let target;
let input;
// When the value of the "focused" store is equal to target,
// call focus() on the input.
focused.subscribe((v) => {
if(v === target) {
input.focus()
// Because subscribe only notifies us of changes,
// we have to reset the store or the focus button
// will stop working.
focused.set('');
}
})
</script>
<span class="searchbar">
<input bind:this={input} />
</span>
focus.svelte
<script>
import { focused } from './store';
export let targeting;
// Use the .set() method to set the value of our `focused` store,
// triggering all our subscribers (like the one in searchbar.svelte).
const focus = () => focused.set(targeting)
</script>
<button on:click={focus}>Focus</button>
App.svelte
<script>
import Searchbar from './searchbar.svelte'
import Focus from './focus.svelte'
// target and targeting must match.
// This allows you to reuse the focus component!
</script>
<Searchbar target="search" />
<Focus targeting="search" />
REPL
It's possible to pass methods down to child components, but it's a little awkward. A more idiomatic approach is to fire an event from the child component and listen for that event from the parent component:
App.html
<div>
<TodoItem
{todo}
on:toggle="toggle(todo)"
/>
</div>
<script>
import TodoItem from './TodoItem.html';
export default {
components: {
TodoItem,
},
methods: {
toggle(todo) {
todo.done = !todo.done;
const { todos } = this.get();
this.set({ todos });
}
}
};
</script>
TodoItem.html
<div>
<button on:click="fire('toggle')">{todo.description}</button>
</div>
If you need to pass an event up through multiple levels of components you can just refire the event...
<TodoItem on:toggle="fire('toggle', event)">...</TodoItem>
...but there's a shorthand for doing so that means the same thing:
<TodoItem on:toggle>...</TodoItem>
The hierarchy is App => Outer => Inner
App:
<script>
import Outer from './Outer.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Outer on:message={handleMessage}/>
Outer:
<script>
import Inner from './Inner.svelte';
</script>
<Inner on:message />
Inner:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
Now, this to me is event bubbling up. If you're trying to do it in a reverse way.. I'm not so sure you can achieve that. Data is passed down, events bubble up.
When it comes to trying to send all events in one big go, there seems to be this svelte github issue created to allow syntax. Sadly it isn't yet implemented.on:*
Just give them a default value.
MyComponent.svelte
<script>
export let i = 123 // Default value is now 123
</script>
<!-- Output is "i = 123" -->
<p>i = {i}</p>
App.svelte
<script>
import MyComponent from './MyComponent.svelte'
</script>
<!-- No error here! -->
<MyComponent/>
So, in your case you would change to export let hide.export let hide = false
You can use two-way data binding by adding App.svelte . Now the value in App.svelte will be updated if the value is changed in Course.svelte.bind:state={course.state}
Here is a REPL:
https://svelte.dev/repl/48649794a7144d63bbde67a2db2f67a9?version=3.24.1There are a much better ways to handle this problem and you should be careful when to use two-way binding. With two-way binding you’ll mess your dataflow very easily.
Better ways to handle dataflow:
storecreateEventDispatcherIf I understand correctly, you are trying to modify data within a component after it is initialized.
When you initialize the component you do something like:
var mything= new Thing({
target: someplace,
data: {text:"some text",status:" works good"}
});
Later on if you need to change the data you can do:
mything.set({text:"new text");
Well... Passing class this way does work for me. See this REPL.
App.svelte
<script>
import MyComponent from './MyComponent.svelte'
let checked
</script>
<label>
<input type=checkbox bind:checked />
Blue?
</label>
<MyComponent class={checked ? 'blue' : 'red'} />
MyComponent.svelte
<script>
export let foo
export let bar
</script>
<pre>foo={foo} bar={bar}</pre>
<button {...$$restProps}>
Some button
</button>
<style>
:global(.red) {
color: red;
}
:global(.blue) {
color: blue;
}
</style>
A couple notes...
Svelte has quite recently introduced (docs -- end of the section), which might be better suited that filtering props manually.$$restProps
This block of code in your example is not reactive:
const {
foo,
bar,
...other
} = $$props;
It is only executed once when the component is created. It is not updated when props change, which might explain why your subsequent change of props () is not reflected.class={condition ? 'one' : 'two'}
If you need to do something like this (say because you need more control than what offers out of the box), you need to put this in a reactive block ($$restProps).$:
For example:
$: ({ foo, bar, ...other } = $$props)
The above JS syntax is equivalent to the following, more verbose, alternative:
let foo
let bar
$: {
foo = $$props.foo
bar = $$props.bar
}