スキップしてメイン コンテンツに移動

[ASP.NET Web Forms][TypeScript] Send data, call functions

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 | MDN

I 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.

コメント

このブログの人気の投稿

[Angular][ASP.NET Core] Upload chunked files

Intro I wanted to send files to Web application (made by ASP.NET Core). If the file size had been small, I didn't need do any special things. But when I tried to send a large file, the error was occurred by ASP.NET Core's limitation. Though I could change the settings, but I didn't want to do that, because I hadn't known the file sizes what would been actually using. So I splitted the data into chunks first, and sent them. After receiving all chunks, I merged them into one file. There might be some libraries or APIs (ex. Stream API) what did them automatically, but I couldn't find them. What I did [ASP.NET Core] Make CORS enabled [Angular] Split a large file into chunks [Angular][ASP.NET Core] Send and receive data as form data [ASP.NET Core] Merge chunks into one file [ASP.NET Core] Make CORS enabled Because the client side application(Angular) and the server side application(ASP.NET Core) had been separated, I had to make CORS(Cross-Origin Requests) ...

[Nest.js] Show static files

Intro I wanted to use Nest.js and WebRTC(node-webrtc). NestJS - A progressive Node.js framework Documentation | NestJS - A progressive Node.js framework And because I wanted to try with simple page(not use JavaScript frameworks), I added static HTML, CSS, JavaScript into a Nest.js project. Prepare Install First, I installed @nestjs/cli. First steps | NestJS - A progressive Node.js framework As same as last time , I couldn't do global install because I had used Volta. But I could installed by volta. volta install @nestjs/cli Create project nest new nest-web-rtc-sample volta pin node@12 Run npm start After doing "npm start", I could getting "Hello World!" from http://localhost:3000. Add static files I could add static files by two ways. @nestjs/serve-static First one of them was using "serve-static". Serve Static | NestJS - A progressive Node.js framework npm install --save @nestjs/serve-static And I needed adding a module into app.modu...

[Angular] Sending file with Observable and showing loading screen

Intro When I tried sending file data on last time, I had confused with "promise.then", "async/await" and "Observable". [Angular][ASP.NET Core] Upload chunked files So I wanted to distinct them, and this time, I tried to use "Observable" because HttpClient return Observable<any>. Call observables in order I sended file in these steps. Read file by FileReader Create directory for saving chunks send and saving chunks merge chunks to one file and delete chunks Each steps used the former steps result. So I could write by Promise.then like below. this.executeStep1() // return Promise<UploadResult> .then(result => this.executeStep2(result)) // return Promise<UploadResult> .then(result => this.executeStep3(result)) // return Promise<UploadResult> .catch(reason => console.log(reason)); Result I could write with pipe & flatMap. file-uploader.service.ts public upload(file: File): Observable<U...