📜 ⬆️ ⬇️

ASP.NET Core, Angular 2, SignalR for Dummies

Hello! I want to share my experience using ASP.Net Core and Angular 2 using SignalR.

Being a 1C programmer, it is often necessary to solve problems that are difficult or impossible to solve on 1C. Knowledge of .Net helps a lot. But as for the client part of the sites, there are many subtleties (JavaScript, CSS, JQuery, etc.) that are quickly forgotten if they are not used.

Angular 2 allows you to significantly simplify the creation of the client side. So TypeScript is much closer to C # (and most importantly allows you to use Ruslish), and it is easy to figure out the templates knowing Razor and Xaml.
')
The main thing that you work with data, by analogy with WPF. At the same time there is a bunch of controls.

I want to share with the same poor fellows like me, or who is just starting to study Angular 2, ASP.Net Core, since I spent considerable time searching for materials for studying.

For training on cats, my 1C Messenger project was chosen to send messages, files and data exchange between 1C users, web pages, mobile applications a la Skype, WhatsApp . Sources Here

Not yet released. Net Core 1.2 and NetStandard 2, now there is no client support for SignalR under .Net Core

So, let's begin. To work we need:

1. ASP.NET Core + Angular 2 Visual Studio Template
2. ASP.NET Core Guide
3. Guide to Angular 2
4. TypeScript Manual
5. Components from PrimeNG
6. Bootstrap components

ASP.NET Core + Angular 2 Visual Studio template is a wonderful template that customizes your applications to use ASP.Net Core and Angular 2 by creating a bunch of json files and insisting on using a webpack. For example, you can read the article Launch Angular2 c Visual Studio 2015

And most importantly, we can change the ts and html files during debugging and see the changes when the page is updated. Classes from Microsoft.AspNetCore.SpaServices.dll are responsible for this.
WebpackDevMiddlewareOptions and SpaRouteExtensions. Yes Yes! We can debug in the browser. At the same time Ts files lie in the folder (no domain)

Plus a lot of examples with the template! But they are all stored in ASCII. If you use Cyrillic, it is tedious to re-save ts and html in UTF-8.

Components from PrimeNG can significantly simplify the creation of client code. In addition, they can explore the creation of your own components and modify existing ones.

Let's start by creating the SignalR hub. Convenient to use typed hubs.

public interface IHelloHub { //         //  Camel  //        Task sendEcho(string str, string ); //   //  ,  Xamarin     4  Task addMessage(string Name, string str, string ConnectionId); } 

That at implementation of this interface I will be controlled not only the sendEcho method. But also Clients.Others.addMessage and Clients.Client (To) .addMessage.

  public class HelloHub : Hub<IHelloHub> { //    ,       //   ,      ID  public async Task sendEcho(string str, string ) { var user = new User(); if (!(user)) return; if (string.IsNullOrWhiteSpace()) await Clients.Others.addMessage(user.Name, str, user.ConnectionId); else await Clients.Client().addMessage(user.Name, str, user.ConnectionId); } } 

In addition, this interface is convenient to use in the .Net application, and you can create a code generator for the TypeScript description of the service.

We now turn to creating a client. First create a common service. One service for all components.

This will allow us to keep the service alive throughout the life of the page, and the component will read data to read data after transitions within the page.

SignalR Service Code
 /// <reference path="../models/ForHelloHub.ts"/> import { IHelloHub, DataInfo, ChatMessage, User} from "../models/ForHelloHub"; import { EventEmitter } from '@angular/core'; import { SelectItem } from 'primeng/primeng'; declare var $: any; export class HelloHub implements IHelloHub { //   public allMessages: ChatMessage[]; //     public connectionExists: Boolean; //    public isRegistered: Boolean; // $.connection.helloHub.server private server: any; // $.connection.helloHub.client private client: any; // $.connection.helloHub private chat: any; // ID  private userId: string; //   public Users: SelectItem[]; //      public onChangeUser: EventEmitter<void> = new EventEmitter<void>(); //     public onAddMessage: EventEmitter<void> = new EventEmitter<void>(); //      public onConnected: EventEmitter<void> = new EventEmitter<void>(); //     . public onRegistered: EventEmitter<void> = new EventEmitter<void>(); constructor() { this.userId = ""; //      "".    //     ,   this.Users = [{ label: "", value: ""}]; this.connectionExists = false; this.isRegistered = false; this.chat = $.connection.helloHub; this.server = this.chat.server; this.client = this.chat.client; //    this.registerOnServerEvents(); this.allMessages = new Array<ChatMessage>(); //    this.startConnection(); } //    .     private sortUsers() { this.Users.sort((a, b: SelectItem) => { if (a.label == "") return -1; return a.label.toLocaleUpperCase().localeCompare(b.label.toLocaleUpperCase()); }); } //      private registerOnServerEvents(): void { let self = this; //     //Task addMessage(string Name, string str, string ConnectionId); this.client.addMessage = (name: string, message: string, ConnectionId: string) => { //    - console.log('addMessage ' + message); self.addMessage(name, message,ConnectionId); }; //     //Task onConnected(string id, string userName, List < User > users); this.client.onConnected = function (id: string, userName: string, allUsers: User[]) { self.isRegistered = true; self.userId = id; //    for (let user of allUsers) { self.addUser(user.ConnectionId, user.Name); } self.sortUsers(); //      self.onRegistered.emit(); }; //Task onNewUserConnected(string id, string userName); //    this.client.onNewUserConnected = (id: string, name: string) => { self.addUser(id, name); }; //Task onUserDisconnected(string id, string Name); //   this.client.onUserDisconnected = (id: string, userName: string) => { let idx = self.Users.findIndex((cur: SelectItem) => { return cur.value == id; }); if (idx != -1) { return self.Users.splice(idx, 1); }; } } //    id //        findUser(userName:string,id: string): SelectItem { let idx = this.Users.findIndex((cur: SelectItem) => { return cur.value == id; }); if (idx != -1) { return this.Users[idx]; } return { label: userName, value:id } } //     addMessage(name: string, message: string, ConnectionId: string): void { this.allMessages.splice(0, 0, new ChatMessage(message, new Date, this.findUser(name, ConnectionId))); this.onAddMessage.emit(); } //       addUser(id: string, name: string): void { if (this.userId === "") return; if (this.userId !== id) { let usr = { label: name, value: id }; this.Users.push(usr); this.sortUsers(); this.onChangeUser.emit(); } } //    private startConnection(): void { let self = this; $.connection.hub.start().done((data: any) => { console.log('startConnection ' + data); self.connectionExists = true; self.onConnected.emit(); console.log('Send onConnected'); }).fail((error) => { console.log('Could not connect ' + error); }); } //=======     //       sendEcho(str: string, : string) { this.server.sendEcho(str, ); } //     sendByName(message: string, : string) { this.server.sendByName(message, ); } //      connect(userName: string) { this.server.connect(userName); } } 


We now turn to the creation of the component:

Component to display received messages and send messages
 import { Component, NgZone, ViewChild, AfterViewInit } from '@angular/core'; import { HelloHub } from '../services/HelloHubServise'; import { ChatMessage } from '../models/ForHelloHub'; import { SelectItem} from 'primeng/primeng'; import { Dropdown2 } from '../Dropdown/Dropdown'; @Component({ selector: 'p-HelloHubComponent', template: require('./SignalRHelloHub.html') }) export class HelloHubComponent { @ViewChild(Dropdown2) public dropdown: Dropdown2; public allMessages: ChatMessage[]; public Users: SelectItem[]; public selectedUser: string; public Message: string; public selectUsername: boolean=false; constructor(private _signalRService: HelloHub) { //      this.subscribeToEvents(); //         this.allMessages = this._signalRService.allMessages; //     this.Users = _signalRService.Users; } //        public sendMessage() { if (this.dropdown.value == "") //   ""   ,   { this._signalRService.sendEcho(this.Message, ""); } else { //  1        if (!this.selectUsername) //     " "     this._signalRService.sendEcho(this.Message, this.dropdown.value); else //        this._signalRService.sendByName(this.Message, this.dropdown.selectedOption.label); } this.Message = ""; } private subscribeToEvents(): void { let self = this; //      this._signalRService.onAddMessage.subscribe(() => { self.allMessages = this._signalRService.allMessages; }); //     this._signalRService.onChangeUser.subscribe(() => { this.Users = self._signalRService.Users; } ); } } 



The component simply reads and updates event data from the Service and sends messages through the service methods. Well, let's show the HTML code of the component.

HTML template

  <div class="container" id="MainMessage"> <form role="form" (ngSubmit)="sendMessage()"> <div class="form-group"> <textarea type="text" [(ngModel)]="Message" name="Message" class="form-control" placeholder=""></textarea> </div> <div class="form-group"> <div class="btn-group open"> <button type="submit" class="btn btn-info"></button> </div> <div class="btn-group" id="users"> <p-dropdown2 [options]="Users" [(ngModel)]="selectedUser" name="dropdownUsers"></p-dropdown2> </div> <div class="btn-group" id="SendByName"> <div class="checkbox"> <label> <input type="checkbox" name="CheckBoxSendByName" [(ngModel)]="selectUsername" [disabled]="dropdown.value==''">   </label> </div> </div> </div> </form> </div> <div class="row"> <div class="col-xs-12 col-md-8" id="GetingMessage"> <template ngFor let-item [ngForOf]="allMessages"> <div class='panel panel-primary'> <div class='panel-heading'> {{item.From.label}}  {{item.Sent.toLocaleString()}} </div> <div class='panel-body'> {{item.Message}} </div> </div> </template> </div> <div class="col-xs-6 col-md-4"> </div> </div> 

We have a form for sending a message, which contains a textarea with the binding property of the Message, a dropdown with information about the connected users, a checkbox with a binding of selectUsername to send the message by name.

And there is a block with allMessages received messages. Html itself turned out to be compact, and all the code is on a very nice TypeScript.

Pay attention to the use of self inside the circuit. This is due to JS. The TS compiler in JS itself generates:

 var _this = this; 

and replaces

 var transport = this.chat.transport; 

on

 var transport = _this.chat.transport; 

But for debugging it is convenient to use an intermediate variable.

  let self = this; 

The source can be found here .

Well and to heap article and the Create great looking templates , fast, mobile apps using JavaScript, Angular 2, and Ionic 2

Angular 2 comes on all fronts

Source: https://habr.com/ru/post/318480/


All Articles