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

Add 'bind-...' for two-way binding #16307

Closed
rynowak opened this issue Mar 28, 2018 · 21 comments
Closed

Add 'bind-...' for two-way binding #16307

rynowak opened this issue Mar 28, 2018 · 21 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components

Comments

@rynowak
Copy link
Member

rynowak commented Mar 28, 2018

Summary

We're adding a feature that replaces @bind(...) with something more first class for tooling.

Introducing bind-... for two-way binding:

<input bind="@CurrentValue" />
@functions {
    public string CurrentValue { get; set; }
}

bind-... attribute implements two-way binding to components and dom elements via a tag helper with special compile-time behavior. This means that intellisense, colorization, and completion know about bind-... and can provide contextual documentation and contextual completion.

image

note: the correct date format is actually yyyy-MM-dd. Do as I say, not as I do 😆

What is two-way binding?

If you're not familiar with @bind (the thing being upgraded), this does two-way binding to both components and dom elements. This is like a macro, it doesn't do anything you couldn't do normally, but it should help you out 👍

Example:

<input bind="@CurrentValue" />
@functions {
    public string CurrentValue { get; set; }
}

Generates something like:

<input value="@CurrentValue" onchange="@((__value) => CurrentValue = __value)/>
@functions {
    public string CurrentValue { get; set; }
}

This means than when the component first runs, the value of the input element will come from the CurrentValue property. When the user types in the textbox, the CurrentValue property will be set to the changed value. This is why we call it two-way binding.

In reality the code generation is a little more complex because bind deals with a few cases of type conversions. But in principle, bind-... will associate the current value of an expression with a value attribute, and will handle changes via a change handler attribute. We expect to invest more in the runtime support in this area in the future, including better conversion, error handler, etc.

One additional point, the expression provided to bind-... should be an LValue, meaning that it's something that can be assigned. Since this is code-generation, it works in places that the C# ref keyword doesn't work.

React can do something similar: https://reactjs.org/docs/two-way-binding-helpers.html

Use cases

DOM elements

The most obvious usage of bind-... is for input elements of various types. You're going to have lots of these 😆

That looks like this:

<input bind="@CurrentValue" />
@functions {
    public string CurrentValue { get; set; }
}

Or this:

<input type="checkbox" bind="@IsSelected" />
@functions {
    public bool IsSelected { get; set; }
}

For cases like this, we have a set of mappings between the structure of an input tag and the attributes that we need to set of the generated dom elements. These mappings are driven by attributes defined in code and are extensible.

Right now this set is minimal, but we plan to provide a good set of mappings out of the box for completion to lead you down the right path.

Format strings

You can also provide a format string - currently only for DateTime values.

<input type="date" bind="@StartDate" format="yyyy-MM-dd" />
@functions {
    public string StartDate { get; set; }
}

The format string will be used to convert to and from .NET values to the DOM attributes. We plan to enhance this area in the future, currently it's restricted to DateTime. If you use this with an expression that isn't a DateTime, expect some fireworks 🎆

Components

bind-... can also enhance components by recognizing component attributes that follow a specific pattern.

That looks like:

@* in Counter.cshtml *@
<div>...html omitted for brevity...</div>
@functions {
    public int Value { get; set; } = 1;
    public Action<int> ValueChanged { get; set; }
}

@* in another file *@
<Counter bind-Value="@CurrentValue" />
@functions {
    public int CurrentValue { get; set; }
}

The code generation for components is a little plainer. Since you're calling into .NET code from .NET code, we expect the types to match.

Note that the component author doesn't have to do anything special to enable this, we detect the bindables based on the names and types of the component properties.

Bind, user-defined

If you have a use for it, you can define your own mappings for bind-... and do things like this:

// in BindAttributes.cs
[BindElement("ul", "foo", "myvalue", "myevent")]
public class BindAttributes
{
}

@* in MyComponent.cshtml *@
<ul bind-foo="@SomeExpression" />

Bind, general case

bind-... also supports a fallback case that totally flexible. You can specify bind with any value attribute name and change handler attribute name.

<ul bind-myvalue-myevent="@SomeExpression" />

This will bind the current value of @SomeExpression to myvalue and a change handler lambda to myevent.

Next steps

The first change here introduces the general concept of bind as a language feature rather than a function call. I think that this works well for the cases that we have tested with @bind, but we need to do more work in the runtime to make it really complete.

This includes:

  • support conditional attributes
  • extend the set of bind-... mappings we provide by default
  • improve support for conversions
  • improve error handling for conversion failures

One of the next things we do will be to give the same treatment to @onclick and extend the set of default mappings we provide for event handlers.

There's also a master list of improvements to the programming model for components described here: https://github.com/aspnet/Blazor/issues/1

That list doesn't have much detail is only a general outline.

@schotime
Copy link

schotime commented Mar 28, 2018

We should also ensure that using a single state store like the FlightFinder demo does works just as well.

If you auto bind to a property then another component depends on that property to render, it does not get re-rendered because you haven't fired the StateHasChanged() event. And thats pretty hard to do without getting into ugly stuff like the below.

<input class="toggle" type="checkbox" @bind(completed) />
@functions
{
    bool completed
    {
        get { return state.Todos[Id].Completed; }
        set { state.Todos[Id].Completed = value; StateHasChanged(); }
    }
}

@darilek
Copy link

darilek commented Mar 28, 2018

IMHO these custom attributes (bind, format, converter, ...) should be prefixed to distinguish them from the standard HTML attributes

@muqeet-khan
Copy link
Contributor

@rynowak I am trying to understand if there is a way for bind to provide ''bindable" data to the compnents ChildContent?

Think of this scenario, for example:

@* Consumer is sending raw data to the component for "processing" *@
<MyStockExample bind-rawstockdata="@rawStockData" bind-processedstockdata="@processedData">
    <textarea rows="5" >
           @processedData
    </textarea>
</MyStockExample>


@functions{
    public ProcessedStockData processedData { get; set; }
    public RawStockData rawStockData { get; set; }
}

Wherein, MyStockExample is taking in RawStockData and then "processing" it and providing it as a bindable object to its child content as (ProcessedStockData) so that the consumer can then use that data without having to depend on the component author.

The question is would the above be possible with this change? If no, is this approach not the right one in context of Blazor components?

@rynowak
Copy link
Member Author

rynowak commented Mar 28, 2018

@muqeet-khan - you don't need bind for that case because it's not two-way, and not changing.

We plan to address this scenario also in the future.

@danroth27
Copy link
Member

@guardrex

@awulkan
Copy link
Contributor

awulkan commented Mar 28, 2018

If this requires manual work to update the DOM after a programmatic change to the property then it's not two-way binding imo. It will confuse a lot of devs coming over from other frameworks. It will also make the code harder to maintain in many cases.

All definitions I can find of two-way binding specify an immediate automatic update of both the rendered output and the stored value upon change, in both directions.

Or are there plans to also implement an auto-propagating two-way binding alternative?

@bdparrish
Copy link
Contributor

<input value="@CurrentValue" onchange="@((__value) => CurrentValue = __value)/>

The code should be moved to JavaScript or the @functions section. Leaving in the HTML will make it difficult to debug in the Devloper Tools. It also clutters the HTML.

@jonathanperis
Copy link

Why not asp-bind like aspnet core existing tag helpers for consistency reasons?

@tdinucci
Copy link
Member

I personally prefer this new "bind-" syntax to the @Bind previously. However is there even a need for the "bind-*" syntax?

I personally think Polymer has a very nice model for bindings.

To me the following transformation doesn't feel intuitive - what does "bind" bind to?. Is it "value", or "id", or "type", etc

<input bind="@CurrentValue" />

to

<input value="@CurrentValue" onchange="@((__value) => CurrentValue = __value)/>

If something like the Polymer syntax were used though then the binding is unambiguous:

<input value="{{CurrentValue}}"/>

I also wonder if things like string formatting (from top post):

<input type="date" bind="@StartDate" format="yyyy-MM-dd" />

Could be achieved with the string interpolation syntax. With the syntax below I don't have to learn anything Blazor specific, I can just use my existing HTML and C# knowledge:

<input type="date" value="@StartDate:yyyy-MM-dd"/>

To take things a little further, the binding syntax could be augmented to signify one/two way bindings. Polymer syntax is:

One-way:

<input value="[[CurrentValue]]"/>

Two-way:

<input value="{{CurrentValue}}"/>
I'm in no way suggesting that you do exactly the same thing as Polymer, I just think there syntax has a lot of nice aspects.

@rynowak
Copy link
Member Author

rynowak commented Mar 29, 2018

The code should be moved to JavaScript or the @functions section. Leaving in the HTML will make it difficult to debug in the Devloper Tools. It also clutters the HTML.

@bdparrish - this is an example of the what the generated code looks like, it's not something we should suggest anyone write.

@bdparrish
Copy link
Contributor

@rynowak

I understand that. I know that the code is simple but code needs to be separated from the presentation.

Also if the generated code looks like that, then it makes it difficult to debug those parts of the HTML.

@krynium
Copy link

krynium commented Mar 29, 2018

Aurelia binding syntax is very intutive, why cant blazor use similar.

http://aurelia.io/docs/binding/basics

@iAmBipinPaul
Copy link

Auerlia data binding syntax is more descriptive and describes it's purpose.

image

http://aurelia.io/docs/binding/basics#html-and-svg-attributes

@rynowak
Copy link
Member Author

rynowak commented Mar 31, 2018

Closing this issue as this feature was introduced with 4407de18baac1ad715549b26d292daa1d2fc3658

Thanks for the feedback and discussion on this topic. We chose this style because it feels very Razor and we're already working with something established (server-side Razor).

@rynowak rynowak closed this as completed Mar 31, 2018
rynowak referenced this issue in dotnet/blazor Apr 10, 2018
This change removes support for the old syntax used for event handlers
and two-way binding.

See the relevant issues for details on the new features and
improvements:

bind https://github.com/aspnet/Blazor/issues/409
event handlers https://github.com/aspnet/Blazor/issues/503

Along with this change we've removed a few additional things Blazor
could do that aren't part of Razor's usual syntax.

----

The features that was used to make something like:
```
<button @OnClick(...) />
```

is an expression that's embedded in a an element's attribute. This
feature might be useful in the future if we want to support 'splatting'
arbitrary attributes into a tag, but the runtime support for this isn't
accessible outside the Blazor core.

----

The features that implement:
```
<button onclick=@{ } />
```

have been removed in favor of a better design for lambdas, method group
conversions and other things for event handler attributes.

use `<button onclick=@(x => ...} />` instead.

We think is a better approach in general, because we want the app
developer to write and see the parameter list.

----

Both syntactic features that have been removed have dedicated error
messages in the compiler. If you're porting old code it should help you
figure out what to do.
rynowak referenced this issue in dotnet/blazor Apr 10, 2018
This change removes support for the old syntax used for event handlers
and two-way binding.

See the relevant issues for details on the new features and
improvements:

bind https://github.com/aspnet/Blazor/issues/409
event handlers https://github.com/aspnet/Blazor/issues/503

Along with this change we've removed a few additional things Blazor
could do that aren't part of Razor's usual syntax.

----

The features that was used to make something like:
```
<button @OnClick(...) />
```

is an expression that's embedded in a an element's attribute. This
feature might be useful in the future if we want to support 'splatting'
arbitrary attributes into a tag, but the runtime support for this isn't
accessible outside the Blazor core.

----

The features that implement:
```
<button onclick=@{ } />
```

have been removed in favor of a better design for lambdas, method group
conversions and other things for event handler attributes.

use `<button onclick=@(x => ...} />` instead.

We think is a better approach in general, because we want the app
developer to write and see the parameter list.

----

Both syntactic features that have been removed have dedicated error
messages in the compiler. If you're porting old code it should help you
figure out what to do.
rynowak referenced this issue in dotnet/blazor Apr 10, 2018
This change removes support for the old syntax used for event handlers
and two-way binding.

See the relevant issues for details on the new features and
improvements:

bind https://github.com/aspnet/Blazor/issues/409
event handlers https://github.com/aspnet/Blazor/issues/503

Along with this change we've removed a few additional things Blazor
could do that aren't part of Razor's usual syntax.

----

The features that was used to make something like:
```
<button @OnClick(...) />
```

is an expression that's embedded in a an element's attribute. This
feature might be useful in the future if we want to support 'splatting'
arbitrary attributes into a tag, but the runtime support for this isn't
accessible outside the Blazor core.

----

The features that implement:
```
<button onclick=@{ } />
```

have been removed in favor of a better design for lambdas, method group
conversions and other things for event handler attributes.

use `<button onclick=@(x => ...} />` instead.

We think is a better approach in general, because we want the app
developer to write and see the parameter list.

----

Both syntactic features that have been removed have dedicated error
messages in the compiler. If you're porting old code it should help you
figure out what to do.
rynowak referenced this issue in dotnet/blazor Apr 10, 2018
This change removes support for the old syntax used for event handlers
and two-way binding.

See the relevant issues for details on the new features and
improvements:

bind https://github.com/aspnet/Blazor/issues/409
event handlers https://github.com/aspnet/Blazor/issues/503

Along with this change we've removed a few additional things Blazor
could do that aren't part of Razor's usual syntax.

----

The features that was used to make something like:
```
<button @OnClick(...) />
```

is an expression that's embedded in a an element's attribute. This
feature might be useful in the future if we want to support 'splatting'
arbitrary attributes into a tag, but the runtime support for this isn't
accessible outside the Blazor core.

----

The features that implement:
```
<button onclick=@{ } />
```

have been removed in favor of a better design for lambdas, method group
conversions and other things for event handler attributes.

use `<button onclick=@(x => ...} />` instead.

We think is a better approach in general, because we want the app
developer to write and see the parameter list.

----

Both syntactic features that have been removed have dedicated error
messages in the compiler. If you're porting old code it should help you
figure out what to do.
@estomagordo
Copy link

Excuse a silly question, but what is the role of the Action in the Counter example? What exactly is required to bind to values in a component?

@Andrzej-W
Copy link

Without Action you have only one way data binding. Also take into account that information in this post is a little outdated. Please read this in the doc:
https://blazor.net/docs/components/index.html#component-parameters
and example you are looking for is below Components attributes subheading.

SteveSandersonMS referenced this issue in SteveSandersonMS/BlazorMigration Nov 27, 2018
This change removes support for the old syntax used for event handlers
and two-way binding.

See the relevant issues for details on the new features and
improvements:

bind https://github.com/aspnet/Blazor/issues/409
event handlers https://github.com/aspnet/Blazor/issues/503

Along with this change we've removed a few additional things Blazor
could do that aren't part of Razor's usual syntax.

----

The features that was used to make something like:
```
<button @OnClick(...) />
```

is an expression that's embedded in a an element's attribute. This
feature might be useful in the future if we want to support 'splatting'
arbitrary attributes into a tag, but the runtime support for this isn't
accessible outside the Blazor core.

----

The features that implement:
```
<button onclick=@{ } />
```

have been removed in favor of a better design for lambdas, method group
conversions and other things for event handler attributes.

use `<button onclick=@(x => ...} />` instead.

We think is a better approach in general, because we want the app
developer to write and see the parameter list.

----

Both syntactic features that have been removed have dedicated error
messages in the compiler. If you're porting old code it should help you
figure out what to do.
@McHeff
Copy link

McHeff commented Feb 21, 2019

Is there a list somewhere that shows the different type's of bindings for different types of controls?

@danroth27
Copy link
Member

@McHeff Take a look at https://docs.microsoft.com/en-us/aspnet/core/razor-components/components?view=aspnetcore-3.0#data-binding and see if it answers your questions.

@McHeff
Copy link

McHeff commented Feb 22, 2019

@McHeff Take a look at https://docs.microsoft.com/en-us/aspnet/core/razor-components/components?view=aspnetcore-3.0#data-binding and see if it answers your questions.

Thanks @danroth27, I've already looked at that page, what I was looking for is like a list of all available types of bindings... Working with Blazor is fun, but I was looking for different types of bindings across different types of controls.

Example. checkboxes have "bind" as well as "bind-value-changed" I believe, so I was wondering what other types there are as intelisense doesn't show them...

Anyways, thanks for the reply man!

Oh, and if you're interested Dan, I've made a discord server for Blazor: https://discord.gg/Xg9ja5s

@sipi41
Copy link

sipi41 commented Jul 17, 2019

I was going crazy with this! the book was saying we should use like this:
<input type="text" bind="@variable" />
but the proper usage should be:
<input type="number" @bind="@variable" />

@phongn105
Copy link

kudos sipi41. I got "Missing attribute name" @variable but still works.

@mkArtakMSFT mkArtakMSFT transferred this issue from dotnet/blazor Oct 27, 2019
@mkArtakMSFT mkArtakMSFT added the area-blazor Includes: Blazor, Razor Components label Oct 27, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

No branches or pull requests