上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件)。这次继续学习Blazor的数据绑定相关的知识。当代前端框架都离不开数据绑定技术。数据绑定技术以数据为主导来驱动UI界面,用户对数据的修改会实时提现在UI上,极大的提高了开发效率,让开发者从繁琐的dom操作中解脱出来。对于数据绑定.NET开发者并不会陌生,WPF里大量应用数据绑定技术,有过WPF开发经验的同学其实很容易理解前端的数据绑定。总之数据绑定技术及其概念、思维极其重要。下面让我们看看Blazor的数据绑定技术。

单向绑定

Blazor的数据绑定官方文档是直接从双向绑定开始的,但我觉得有必要说一下单向绑定。因为其他框架一般都会区分单向、双向,比如vue的v-bind单向,v-model就是双向。我们这里分开讲也有利于跟其他框架进行对比。下面我们实现一个计数器组件来演示下单向数据绑定。

使用@进行绑定

@page "/counter"

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0; private void IncrementCount()
{
currentCount++;
}
}

这个Counter组件默认的项目就自带。跟我们使用服务端Razor一样,使用@符号在需要替换值的地方插入对应的变量。这个值就会被渲染在相应的地方。当我们在前端修改变量的时候,对应的ui界面会同步进行修改。

使用@bind-{attribute}进行绑定

除了直接使用@进行绑定,我们还可以使用@bind-{attribute}来实现对html元素属性的绑定,比如对style,class内容进行绑定。下面演示下对class进行绑定。我们把p元素的class绑定到“currentClass”字段。

@page "/counter"

<h1>Counter</h1>

<p @bind-class="currentClass" @bind-class:event="onchange">
current count: @currentCount
</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code {
private string currentClass = "text-danger"; private int currentCount = 0; private void IncrementCount()
{
currentCount++;
}
}

使用@bind-{attribute}进行绑定有个比较奇怪的问题,当你使用@bind-{attribute}进行绑定的时候必须同时指定@bind-{attribute}:event。@bind-{attribute}:event是用来指定双向绑定的时候控件在发生某个事件的时候回写值到绑定的字段上。可是p,div这种元素根本不可能会激发onchange,oninput这种事件,也不可能去修改绑定的字段的值,这个用法感觉有点多此一举。

Blazor的单向数据绑定的用法跟ASP.NET Core MVC的Razor基本相似,不同点就是Blazor不需要Http回发到服务器就可以实时渲染新的界面出来。

双向绑定

双向绑定主要使用在一些输入控件上,比如input,select等。当我们对这些控件上的值进行修改后会回写绑定的字段。这种特性在表单场景中非常有用。我们定义一个用户信息编辑的组件来演示下:

@page "/infoedit"

<p>
userName: @userName
</p>
<p>
sex: @sex
</p>
<p>
userName: <input @bind="userName" />
</p>
<p>
sex:
<select @bind="sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
</p> @code {
private string userName="abc";
private string sex="f";
}

当我们运行这个组件,在文本框进行修改后,鼠标点击其他地方让文本框失去焦点值就会回写到绑定的字段上,上面的单向绑定信息会自动同步。但是如果你用过VUE或者Angularjs的双向绑定就会觉得失去焦点再回写字段数据太慢了,一点也不酷。要知道VUE的双向绑定可是实时同步的,那么Blazor如何做到在输入的同时就更新值呢,答案是使用@bind:event来指定回写的激发事件,我们改成“oninput”事件就可以实现:

<p>
userName: <input @bind="userName" @bind:event="oninput"/>
</p>

双向绑定的多种写法

看到这里也许你也明白了,@bind真正的本质是由对value的绑定和对某个事件的绑定协同完成的。这点跟VUE非常相似。@bind其实是@bind-value的缩写,我们可以用@bind-value来实现双向绑定:

<p>
userName: <input @bind-value="userName" @bind-value:event="oninput"/>
</p>

以上写法的效果跟@bind一模一样。再进一步,@bind-value也只是对@的包装,我们可以使用@来实现双向绑定:

@page "/infoedit"

<p>
userName: @userName
</p>
<p>
sex: @sex
</p>
<p>
userName: <input value="@userName" @oninput="oninput"/>
</p>
<p>
sex:
<select @bind="sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
</p> @code {
private string userName="abc";
private string sex="f"; private void oninput(ChangeEventArgs e)
{
userName = e.Value.ToString();
} }

以上代码的效果跟@bind一模一样。通过使用@对value直接进行绑定以及绑定一个oninput事件进行值的回写,同样实现了双向绑定。

格式化时间字符串

使用@bind:format 可以对绑定时间类型字段的时候进行格式化:

出生日期:<input @bind="birthDay" @bind:format="yyyy-MM-dd" />

这个功能有点类似Angularjs的filter功能,但是目前只能对时间进行格式化,功能很弱。

父组件绑定数据到子组件

组件之间往往都是嵌套的,很多子组件都依赖父组件的数据来决定如何呈现,这种场景非常常见。我们还是继续修改上面的编辑组件,用户信息不在自己初始化,而是从父组件传递过来:

子组件:

====================child==================

<p>
userName: <input @bind="UserInfo.UserName" />
</p>
<p>
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
</p> <p>
BrithDay:<input @bind="UserInfo.BrithDay" />
</p>
@code { [Parameter]
public UserInfo UserInfo { get; set; } [Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; }
}

子组件定义一个UserInfo对象并且使用[Parameter]进行标记,同时如果父组件使用@bind-UserInfo来绑定的话,还必须实现一个UserInfoChanged事件。

父组件:

@page "/"
====================parent================== <p>
userName: @userInfo.UserName
</p>
<p>
sex: @userInfo.Sex
</p>
<p>
brithday: @userInfo.BrithDay
</p> <InfoEdit @bind-UserInfo="userInfo"></InfoEdit> @code { private UserInfo userInfo; protected override void OnInitialized()
{
userInfo = new UserInfo
{
UserName = "abc",
Sex = "f",
BrithDay = DateTime.Now
};
base.OnInitialized();
}
}



父组件初始化一个UserInfo对象后通过@bind-UserInfo绑定给子组件。注意这里我们修改子组件的值并不会同步给父组件,所以可以看到@bind-UserInfo的传值还是单向的。

子组件传值给父组件 ??

原来我以为父组件使用@bind-UserInfo并且子组件实现了对应的changed方法就可以实现子组件跟父组件的自动传值,就跟input的双向绑定一样。但是不管我怎么试都没有卵用。如果只是单向的那为什么要这么大费周章?我直接使用属性赋值不就可以了么?像下面这样:

<InfoEdit UserInfo="userInfo" ></InfoEdit>

直接通过组件的属性直接把父组件的数据传递到子组件,效果跟上面是一样的,而且这样子组件我还能少写一个changed事件。我原本以为使用基本类型,比如string可以自动双向绑定,然后并没有什么卵用。没有办法我继续尝试父组件监听UserInfoChanged事件来接受子组件的数据,然后VS提示我同一个事件不能绑定两次。

我已经无语了,难道要我再定义一个事件吗?于是我放弃了@bind-来实现子组件给父组件传值,我直接使用属性赋值难道不比这个简单吗?

子组件修改数据的时候不断对外抛事件:

====================child==================

<p>
userName: <input @bind="UserInfo.UserName" @oninput="InvokeChanged"/>
</p> <p>
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
</p> <p>
BrithDay:<input @bind="UserInfo.BrithDay" />
</p>
@code { [Parameter]
public UserInfo UserInfo { get; set; } [Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; } private void InvokeChanged()
{
UserInfoChanged.InvokeAsync(this.UserInfo);
Console.WriteLine("InvokeChanged");
} }

父组件监听事件后更新数据:

@page "/"
====================parent```================== <p>
userName: @userInfo.UserName
</p>
<p>
sex: @userInfo.Sex
</p>
<p>
brithday: @userInfo.BrithDay
</p>
<p>
title: @title
</p> <InfoEdit UserInfo="userInfo" UserInfoChanged="HandleUserInfoChanged"></InfoEdit> @code { private UserInfo userInfo; private string title; protected override void OnInitialized()
{
userInfo = new UserInfo
{
UserName = "abc",
Sex = "f",
BrithDay = DateTime.Now
};
base.OnInitialized();
} private void HandleUserInfoChanged(UserInfo info)
{
this.userInfo.UserName = info.UserName; Console.WriteLine("HandleUserInfoChanged");
} }

我原以为这样就没什么问题了,可奇怪的是,父组件页面重新渲染需要在子组件第二次修改数据后呈现且呈现的是前一次的。


到这里我已经无语了,最后我只能在子组件直接添加一个按钮,修改完后点击保存来触发InvokeChanged事件,这样子是可以的:

====================child==================

<p>
userName: <input @bind="UserInfo.UserName" />
</p> <p>
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
</p> <p>
BrithDay:<input @bind="UserInfo.BrithDay" />
</p> <button class="btn btn-danger" @onclick="InvokeChanged">保存</button> @code { [Parameter]
public UserInfo UserInfo { get; set; } [Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; } private void InvokeChanged()
{
UserInfoChanged.InvokeAsync(this.UserInfo);
Console.WriteLine("InvokeChanged");
} }

到此数据绑定也演示完了,可是关于子组件往父组件传值的事我实在没像明白,难道是我哪里错了?

最后附上代码:BlazorWasmDataBind

ASP.NET Core Blazor Webassembly 之 数据绑定的更多相关文章

  1. ASP.NET Core Blazor Webassembly 之 路由

    web最精妙的设计就是通过url把多个页面串联起来,并且可以互相跳转.我们开发系统的时候总是需要使用路由来实现页面间的跳转.传统的web开发主要是使用a标签或者是服务端redirect来跳转.那今天来 ...

  2. ASP.NET Core Blazor Webassembly 之 组件

    关于组件 现在前端几大轮子全面组件化.组件让我们可以对常用的功能进行封装,以便复用.组件这东西对于搞.NET的同学其实并不陌生,以前ASP.NET WebForm的用户控件其实也是一种组件.它封装ht ...

  3. [Asp.Net Core] Blazor WebAssembly - 工程向 - 如何在欢迎页面里, 预先加载wasm所需的文件

    前言, Blazor Assembly 需要最少 1.9M 的下载量.  ( Blazor WebAssembly 船新项目下载量测试 , 仅供参考. ) 随着程序越来越复杂, 引用的东西越来越多,  ...

  4. ASP.NET Core Blazor WebAssembly 之 .NET JavaScript互调

    Blazor WebAssembly可以在浏览器上跑C#代码,但是很多时候显然还是需要跟JavaScript打交道.比如操作dom,当然跟angular.vue一样不提倡直接操作dom:比如浏览器的后 ...

  5. ASP.NET Core Blazor Webassembly 之 渐进式应用(PWA)

    Blazor支持渐进式应用开发也就是PWA.使用PWA模式可以使得web应用有原生应用般的体验. 什么是PWA PWA应用是指那些使用指定技术和标准模式来开发的web应用,这将同时赋予它们web应用和 ...

  6. ASP.NET Core Blazor WebAssembly实现一个简单的TODO List

    基于blazor实现的一个简单的TODO List 最近看到一些大佬都开始关注blazor,我也想学习一下.做了一个小的demo,todolist,仅是一个小示例,参考此vue项目的实现http:// ...

  7. ASP.NET Core Blazor 初探之 Blazor Server

    上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly).这次来看看Blazor Server该怎么玩. ...

  8. ASP.NET Core Blazor 用Inspinia静态页模板搭建简易后台(实现菜单选中)

    Blazor 是一个用于使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建丰富的交互式 UI. 共享使用 .NET 编写的服务器端和客户端应用逻辑 ...

  9. 学习ASP.NET Core Blazor编程系列六——新增图书(上)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

随机推荐

  1. P3467(矩形覆盖问题)

    描述:https://www.luogu.com.cn/problem/P3467 1.考虑如果整个建筑物链是等高的,一张高为链高,宽为整个建筑物宽的海报即可完全覆盖: 2.若有两个不等高的元素组成建 ...

  2. Northwestern European Regional Contest 2014 Gym - 101482

    Gym 101482C Cent Savings 简单的dp #include<bits/stdc++.h> #define inf 0x3f3f3f3f #define inf64 0x ...

  3. Polycarp and Div 3 CodeForces - 1005D

    这个题目其实很简单,有很多的方法写,然后我还是不会写,感觉自己好菜, 我开始想的是dp,但是不知道怎么dp,看了网上题解,豁然开朗 dp[i] 表示前面i个数满足条件的数有多少,f[s]表示前缀和为s ...

  4. java运行时跟编译时的区别,欢迎大家指正

    个人博客地址:https://blog.csdn.net/qq_41907991 关于java运行时及编译时期的区别: 首先我们要了解编译以及运行的概念: 编译就是指,编译器帮你把源码翻译成机器能识别 ...

  5. 001_python变量,if,while

    Python介绍 python的出生与应用 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时间, ...

  6. Linux内核驱动学习(九)GPIO外部输入的处理

    文章目录 前言 设备树 两个结构体 gpio_platform_data gpio_demo_device 两种方式 轮询 外部中断 总结 附录 前言 前面是如何操作GPIO进行输出,这里我重新实现了 ...

  7. [csu/coj 1080]划分树求区间前k大数和

    题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数 ...

  8. spark优化总结

    1.Spark调优背景 目前Zeppelin已经上线一段时间,Spark作为底层SQL执行引擎,需要进行整体性能调优,来提高SQL查询效率.本文主要给出调优的结论,因为涉及参数很多,故没有很细粒度调优 ...

  9. Java SPI机制简述

    概述 SPI全称Service Provider Interface,是一种为框架提供良好扩展性的机制.一般由框架开发方定义接口规范(如java.sql.Driver),而第三方厂商为之提供自己的实现 ...

  10. JavaWeb学习之JSP(二) JSP标签

    JSP常用标签 什么是JSP标签 JSP标签,有的地方也叫做JSP动作,在JSP中编写大量的java代码会使JSP页面显得杂乱无章,看起来非常不舒服,因此JSP提供了一些类似html的标签,通过这些标 ...