Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing value(s) of an object with this throws error. #4153

Closed
roket1428 opened this issue Dec 23, 2019 · 9 comments
Closed

Accessing value(s) of an object with this throws error. #4153

roket1428 opened this issue Dec 23, 2019 · 9 comments
Labels
awaiting submitter needs a reproduction, or clarification

Comments

@roket1428
Copy link

I was trying to access all values of all fields that I can have in a certain page. For example username field's value, password field's value, firstname field's value and lastname field's value and so on. With the approach in my mind, I could access the values of all fields in a certain page and I can manipulate every individual error messages based on those values like I won't allow users to have same values in username field and password field. But, as you can see at the demo, it doesn't work as I expected. So I'm looking for possible solutions or better approach than mine. :)
Demo: https://svelte.dev/repl/ac89d68f4047497f96bc0fac88fd2fd1?version=3.16.5

<!-- App.svelte -->
<script>
	import Input from "./Input.svelte";
	
	let username = {
		value: "",
		error: "",
		validate() {
			this.error = (this.value === "asdf") 
			  ? "ERROR MESSAGE" 
			  : "";
		},
	};
</script>

<Input 
  bind:value={username.value}
  error={username.error}
  on:blur={username.validate}
/>
<!-- Input.svelte -->
<script>
	export let value;
	export let error;
</script>

<input {value} on:input={e => value = e.target.value} on:blur>
{#if error}<p>{error}</p>{/if}

<style>
	input {
		display: block;
	}
</style>
@tcrowe
Copy link
Contributor

tcrowe commented Dec 23, 2019

I looked at it and the generated code binds to a different context:

Screen Shot 2019-12-23 at 2 31 30 PM

@Conduitry
Copy link
Member

It works if you do on:blur={() => username.validate()}. I'm not sure what context I'd expect component event handlers to be called with. Probably with the component instance? It looks like it's undefined now, which is probably a bug.

@Conduitry Conduitry added bug awaiting submitter needs a reproduction, or clarification labels Dec 23, 2019
@roket1428
Copy link
Author

roket1428 commented Dec 23, 2019

It works if you do on:blur={() => username.validate()}. I'm not sure what context I'd expect component event handlers to be called with. Probably with the component instance? It looks like it's undefined now, which is probably a bug.

on:blur={username.validate} feels more natural but if you think this is the solution, I'll close this issue.

@Conduitry
Copy link
Member

Conduitry commented Dec 24, 2019

No, I think there's probably a bug here - but that's a simple workaround you can use for now. Actually, I'm not sure changing the behavior here would help in this case, if we make the this be something else. It'll probably always be safer to just use () => username.validate(), but there is more to look into here. It might make sense to always introduce a closure when compiling these, like we do with DOM event handlers. I'm not sure.

@tanhauhau
Copy link
Member

tanhauhau commented Dec 24, 2019

No. this is expected. you need to bind your callback function if you want to get the this as username:

<Input on:blur={username.validate.bind(username)} />

This is not about Svelte actually, you can look at this CodeSandbox, telling you the difference between a bound and unbound callback function.

a working repl

do note that, assigning this.error is not reactive, ie: it will not reactively update your DOM. but that's a different "problem".

@roket1428
Copy link
Author

No. this is expected. you need to bind your callback function if you want to get the this as username:

<Input on:blur={username.validate.bind(username)} />

This is not about Svelte actually, you can look at this CodeSandbox, telling you the difference between a bound and unbound callback function.

a working repl

do note that, assigning this.error is not reactive, ie: it will not reactively update your DOM. but that's a different "problem".

I see the difference very clearly, thanks a lot but using on:blur={<fieldname>.validate.bind(<fieldname>)] makes its error field reactive, are you sure about this?

@tanhauhau
Copy link
Member

no. on:blur={<fieldname>.validate.bind(<fieldname>)], makes the this inside validate refers to <fieldname>

@roket1428
Copy link
Author

So, you are saying that the error property isn't reactive, right?

@divmgl
Copy link

divmgl commented Dec 28, 2019

@roket1428 this.error from inside of the function is not reactive because the this keyword is not reactive. As in, Svelte won't add $$invalidate. If you use .bind(username), you're dynamically binding the object username to this, so it makes sense that Svelte would not know what to invalidate. Here's a REPL snippet demonstrating this:

https://svelte.dev/repl/2b6e48f9cce14134b36aee50d2fb80b6?version=3.16.5

From my understanding, Svelte uses variables to understand when something is reactive. Here's an example that doesn't use the this keyword.

https://svelte.dev/repl/226b2b1d68f54c9dbc50ca4301b2ac7b?version=3.16.7

You might want to avoid nested functions inside of objects that use the this keyword if you want to stay reactive.

@Conduitry Conduitry removed the bug label Jan 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting submitter needs a reproduction, or clarification
Projects
None yet
Development

No branches or pull requests

5 participants