Intro
Recently, I often update ASP.NET Web Forms application in my work.Because Web Forms won't be supported in .NET 5, so I want to replace them into MVC.
Introducing .NET 5 | .NET Blog
I hope I can rebuild the projects from the beginning, but it's a little difficult.
The reasons are like the bugets, there are many "Big ball of mud", and so on.
So now, I try to organize to reduce burden for replace in the future.
This time, to change from Web Forms to HTML(reduce the Web Forms functions from View), I try to interact between Web Forms(+ code behind) and TypeScript.
Environments
- ASP.NET ver.4.5.2
- TypeScript ver.3.8.3
- webpack ver.4.42.1
package.json
{
"scripts": {
"webpack": "npx webpack -w"
},
"dependencies": {
"rxjs": "^6.5.4",
"ts-loader": "^6.2.2",
"tsc": "^1.20150623.0",
"typescript": "^3.8.3",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"whatwg-fetch": "^3.0.0"
}
}
webpack.config.js
var path = require('path');
module.exports = {
mode: 'development',
entry: {
'main': './src/ts/main-page.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, './dist/js'),
library: 'Page',
libraryTarget: 'umd'
}
}
Call from Web Forms(code behind)
I can call JavaScript functions from code behind of Web Forms in several ways.DOM events
I can call JavaScript functions by button of Web Forms.Default.aspx
<%@ Page Title="Home Page" Language="VB" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.vb" Inherits="WebApplication1._Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<p>
<asp:TextBox ID="AspSampleText" runat="server">Hello</asp:TextBox>
<asp:Button ID="CallSampleButton" runat="server" Text="Button" />
</p>
<script type="text/javascript" src="dist/js/main.bundle.js"></script>
</asp:Content>
Default.aspx.vb
Imports System.IO
Imports Newtonsoft.Json
Public Class _Default
Inherits Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim message As New Message
message.Message = "Hello Button!"
Dim jsonMessage = JsonConvert.SerializeObject(message)
CallSampleButton.OnClientClick = "return Page.callFromButton(" & jsonMessage & ")"
End Sub
End Class
Message.vb
Public Class Message
Public Property Message As String
End Class
I create JSON string by Newtonsoft.Json to set arguments.Serialize an Object - Json.NET Documentation
main-page.ts
import { Message } from "./message";
export function callFromButton(message: Message): string {
console.log(`Call button ${message}`);
return 'hello button';
}
message.ts
export interface Message {
message: string;
}
It's very simple to write. But I have problems caused by "Post Back".When I click the button, althogh the event will be fired, but the page will be reloaded.
And some JavaScript codes will be removed.
And even if I don't use the button of ASP.NET(asp:Button), if I use button tag(<button>), the "Post Back" will be fired.
So I can't use buttons on aspx.
RegisterStartupScript
Another way to call JavaScript functions is using RegisterStartupScript.ClientScriptManager.RegisterStartupScript Method - Microsoft Docs
Default.aspx.vb
Imports System.IO
Imports Newtonsoft.Json
Public Class _Default
Inherits Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim scriptManager As ClientScriptManager = Page.ClientScript
Dim myType As Type = Me.GetType()
If Not scriptManager.IsStartupScriptRegistered(myType, "StartupSample") Then
Dim startupMessage As New Message
startupMessage.Message = "Hello Startup!"
Dim jsonStartupMessage = JsonConvert.SerializeObject(startupMessage)
Dim registerScript = GenerateCallMethodScript("callFromStartup", jsonStartupMessage)
scriptManager.RegisterStartupScript(myType, "StartupSample", registerScript)
End If
End Sub
Private Function GenerateCallMethodScript(methodName As String, jsonArgument As String) As String
Dim builder As New StringBuilder
With builder
.Append("<script type=""text/javascript"">")
.Append("Page." & methodName & "(" & jsonArgument & ");")
.Append("</script>")
End With
Return builder.ToString()
End Function
End Class
If I want to use script block like below, I can use RegisterClientScriptBlock.
ClientScriptManager.RegisterClientScriptBlock Method - Microsoft Docs
Default.aspx.vb
Imports System.IO
Imports Newtonsoft.Json
Public Class _Default
Inherits Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim scriptManager As ClientScriptManager = Page.ClientScript
Dim myType As Type = Me.GetType()
If Not scriptManager.IsStartupScriptRegistered(myType, "StartupSample") Then
Dim registerScript = GenerateCallMethodScript("callFromStartup")
scriptManager.RegisterClientScriptBlock(myType, "StartupSample", registerScript)
End If
End Sub
Private Function GenerateCallMethodScript(methodName As String) As String
Dim builder As New StringBuilder
With builder
.Append("<script type=""text/javascript"">")
.Append("function " & methodName & " () { alert('Hello'); }")
.Append("</script>")
End With
Return builder.ToString()
End Function
End Class
Call from TypeScript (JavaScript)
I can use "Generic Handler" to call ASP.NET functions from TypeScript.Generic Handler ashx file : Post send JSON data in Asp.net c#, jQuery | Codepedia
SampleHandler.ashx
<%@ WebHandler Language="VB" CodeBehind="SampleHandler.ashx.vb" Class="WebApplication1.SampleHandler" %>
SampleHandler.ashx.vb
Imports System.IO
Imports System.Web
Imports System.Web.Services
Imports Newtonsoft.Json
Public Class SampleHandler
Implements System.Web.IHttpHandler
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim gotValue As String = New StreamReader(context.Request.InputStream).ReadToEnd()
If String.IsNullOrEmpty(gotValue) Then
Console.WriteLine("Failed")
Else
Dim message As Message = JsonConvert.DeserializeObject(Of Message)(gotValue)
If IsNothing(message) Then
Console.WriteLine("FailedConvert")
Else
Console.WriteLine("OK " & message.Message)
End If
End If
context.Response.ContentType = "text/plain"
context.Response.Write("Hello World!")
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
main-page.ts
import { Message } from "./message";
import 'whatwg-fetch';
async function callGenericHandler() {
await window.fetch('/SampleHandler.ashx',
{
mode: 'cors',
method: 'POST',
headers: {
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify({message: 'Hello General Handler!'})
})
.then((response) => console.log(`RESPONSE: ${response}`))
.catch((error) => console.error(error));
}
callGenericHandler();
Using Fetch - Web APIs | MDNI can call "Generic Handler" to interact DB. It's like accessing controller of MVC.
But how about if I want to feedback to code behind(aspx.vb)?
I can call aspx instead of ashx.
But when I call it, the page will be reloaded.
So if I want to use TypeScipt, I think I have to remove all events of ASP.NET.
コメント
コメントを投稿