TypeError: login is not a function at HTMLInputElement.onclick (VM188 index.html:76) onclick @ VM188 index.html:76

I am trying to make a login page for my project I am working on and I came across this error while trying to run the code. I have no idea what is causing it.

Here is my code:

function login() {
      var userEmail = document.getElementById("email").value;
      var userPass = document.getElementById("password").value;
      
      window.alert(userEmail + " " + userPass);
}
<input name="login" id="login" class="btn btn-block login-btn mb-4" type="button" value="Login" onclick="login()">

Do you know what’s happening?

Thanks

Image panning of the image using the FabricJs

I am trying to implement the panning of the image while the image moves after double click on the image. It’s working fine for the non-rotated images. It’s not working for the rotated images. Here is the link to my code jsfiddle

canvas.observe('object:moving', function(e) {
        var movingBox = e.target;
  movingBox.setCoords();
        
  var top = movingBox.oCoords.tl.y;
  var bottom = top + movingBox.height;
  var left = movingBox.oCoords.tl.x;
  var right = left + movingBox.width;
  
  var boundingBox = picArea;
 boundingBox.setCoords();

  var topBound = boundingBox.oCoords.tl.y;
  var bottomBound = topBound + boundingBox.height;
  var leftBound = boundingBox.oCoords.tl.x;
  var rightBound = leftBound + boundingBox.width;
 movingBox.left = (Math.max(Math.min(left, leftBound), rightBound - movingBox.width));
 movingBox.top = (Math.max(Math.min(top, topBound), bottomBound - movingBox.height));
    }
   
);

HttpRequest still gets the old parameter before window.history.replaceState

I have a Sharepoint onprem server side webpart and I would need to replace my query parameter without refreshing the page so I used

if (history.pushState) {
            var newurl = "<%=getURL()%>" + "/SitePages/searchresults.aspx?searchtext=" + searchText;
            window.history.pushState({ path: newurl }, '', newurl);
        }

now this would trigger a button click, and a postback would happen which refreshes the contents of my page without reloading it. it needs to get the query string parameter from the URL. its working when i use

HttpContext.Current.Request.UrlReferrer.AbsoluteUri;

but I am not sure if this is the correct way to do it. and I noticed that in the browser network tab, the xhr call is still the old query parameter before i changed it via pushState.

enter image description here

Is there a way that I can change the URL and capture it in the code behind in asp?

Uncaught Invariant Violation: addComponentAsRefTo(…): Only a ReactOwner can have refs. existing answer not helping

I am using legacy appltion of react. I did some update and installed some dependecies like i18next react-i18next but I am getting below error now.

enter image description here

below is my file webpack.config.js

const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const extractCSS = new ExtractTextPlugin('[name].fonts.css');
const extractSCSS = new ExtractTextPlugin('[name].styles.css');

const BUILD_DIR = path.resolve(__dirname, 'build');
const SRC_DIR = path.resolve(__dirname, 'src');

// Try the environment variable, otherwise use root
const ASSET_PATH = process.env.ASSET_PATH || '/';

module.exports = (env = {}) => {
  return {
    entry: {
      index: [SRC_DIR + '/index.js'],
    },
    output: {
      path: BUILD_DIR,
      filename: '[name].bundle.js',
      publicPath: ASSET_PATH,
    },
    resolve: {
      alias: {
        react: path.join(__dirname, 'node_modules', 'react'),
      },
      extensions: ['', '.js'],
    },
    // watch: true,
    // devtool: 'inline-source-map',
    devServer: {
      contentBase: BUILD_DIR,
      //   port: 9001,
      compress: true,
      hot: true,
      open: true,
      watchOptions: {
        // Delay the rebuild after the first change
        aggregateTimeout: 300,
        ignored: /node_modules/,
        // Poll using interval (in ms, accepts boolean too)
        poll: 1000,
      },
    },
    module: {
      rules: [
        {
          test: /.(js|jsx)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              presets: ['es2015', 'react'],
              plugins: ['transform-class-properties'],
            },
          },
        },
        {
          test: /.html$/,
          loader: 'html-loader',
        },
        {
          test: /.(scss)$/,
          use: ['css-hot-loader'].concat(
            extractSCSS.extract({
              fallback: 'style-loader',
              use: [
                {
                  loader: 'css-loader',
                  options: { alias: { '../img': '../public/img' } },
                },
                {
                  loader: 'sass-loader',
                },
              ],
            })
          ),
        },
        {
          test: /.css$/,
          use: extractCSS.extract({
            fallback: 'style-loader',
            use: 'css-loader',
          }),
        },
        {
          test: /.(png|jpg|jpeg|gif|ico)$/,
          use: [
            {
              // loader: 'url-loader'
              loader: 'file-loader',
              options: {
                name: './img/[name].[hash].[ext]',
              },
            },
          ],
        },
        {
          test: /.(woff(2)?|ttf|eot|svg)(?v=d+.d+.d+)?$/,
          loader: 'file-loader',
          options: {
            name: './public/fonts/[name].[hash].[ext]',
          },
        },
      ],
    },
    resolve: {
      extensions: ['.js', '.json', '.jsx', '.css', '.scss'],
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.optimize.UglifyJsPlugin({ sourceMap: true }),
      new webpack.NamedModulesPlugin(),
      extractCSS,
      extractSCSS,
      new HtmlWebpackPlugin({
        inject: true,
        template: './public/index.html',
        favicon: './public/favicon.png',
      }),
      new CopyWebpackPlugin([{ from: './public/img', to: 'img' }], {
        copyUnmodified: false,
      }),
      new webpack.EnvironmentPlugin(['API_URL', 'FRONT_URL']),
    ],
  };
};

Below is package.json file.

"dependencies": {
    "axios": "^0.17.1",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "babel-preset-env": "^1.6.1",
    "bootstrap": "^3.3.7",
    "bowser": "^2.1.0",
    "classnames": "^2.1.3",
    "copy-webpack-plugin": "^4.2.1",
    "core-js": "^2.5.4",
    "create-react-class": "^15.7.0",
    "css-hot-loader": "^1.3.4",
    "css-loader": "^0.28.7",
    "draft-js": "^0.10.5",
    "extract-text-webpack-plugin": "^3.0.2",
    "fbemitter": "^2.0.0",
    "file-loader": "^1.1.5",
    "font-awesome": "^4.7.0",
    "html-loader": "^0.5.1",
    "html-react-parser": "^0.4.2",
    "html-webpack-plugin": "^2.30.1",
    "html2canvas": "^1.0.0-alpha.10",
    "i18next": "^21.6.3",
    "jquery": "^3.2.1",
    "jspdf": "^1.3.5",
    "moment": "^2.13.0",
    "prop-types": "^15.7.2",
    "randomstring": "^1.1.5",
    "react": "^17.0.2",
    "react-addons-test-utils": "^15.6.0",
    "react-addons-update": "^15.6.0",
    "react-anything-sortable": "^1.7.2",
    "react-bootstrap": "^0.31.5",
    "react-bootstrap-date-picker-thecodingmachine": "^5.0.1",
    "react-bootstrap-native-slider": "^2.0.1",
    "react-bootstrap-slider": "^2.0.0",
    "react-bootstrap-table": "^4.1.5",
    "react-color": "^2.13.8",
    "react-datepicker": "1.6.0",
    "react-dropzone": "^4.2.3",
    "react-i18next": "^11.15.1",
    "react-modal": "^3.1.2",
    "react-router-dom": "^4.2.2",
    "react-scrollable-anchor": "^0.6.1",
    "react-select": "^1.0.0-rc.5",
    "react-signature-pad": "^0.0.6",
    "react-spinners": "^0.4.5",
    "react-star-rating": "^1.4.2",
    "react-stepzilla": "^4.6.3",
    "react-textarea-autosize": "^5.1.0",
    "react-toastify": "^3.1.0",
    "react-toggle": "^4.0.2",
    "react-transition-group": "^1.2.1",
    "reactjs-localstorage": "0.0.5",
    "reactstrap": "^4.8.0",
    "reflux": "^6.4.1",
    "style-loader": "^0.19.0",
    "superagent": "^3.8.1",
    "transform-class-properties": "^1.0.0-beta",
    "url-loader": "0.6.2",
    "xss": "^0.3.4"
  },
  "peerDependencies": {
    "react": "^16.0.0",
    "react-router-dom": "^4.2.2"
  },
  "devDependencies": {
    "babel": "6.23.0",
    "babel-core": "6.26.0",
    "babel-loader": "7.1.2",
    "babel-preset-es2015": "6.24.1",
    "babel-preset-react": "6.24.1",
    "babel-register": "6.26.0",
    "canvas": "1.6.7",
    "css-loader": "^0.28.7",
    "dotenv-cli": "2.0.1",
    "draftjs-to-html": "^0.8.3",
    "immutable": "^3.8.1",
    "jsdom": "11.2.0",
    "mocha": "^3.3.0",
    "mocha-jsdom": "^1.1.0",
    "node-libs-browser": "^2.0.0",
    "node-sass": "^4.5.3",
    "react-draft-wysiwyg": "1.10.12",
    "react-hot-loader": "^1.2.7",
    "react-tools": "0.13.3",
    "sass-loader": "^6.0.6",
    "source-map": "^0.6.1",
    "style-loader": "^0.18.2",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.4"
  }

what mistake I am doing?

How to keep function not fail even if some async job fails?

I’m making a backend server with Node.js (based on Nest.js). Each route handler mainly queries/modifies the database (main effect), and sometimes send events to external server, leave log and etc (side effect).

I want each route handler functions not to fail when only side effect throws error and main effect goes well(behavior 1). And I also want the side effects to be executed in parallel with the main effect so that the time executing each route handler functions can be decreased(behavior 2).

How can I achieve this behavior in javascript? I first tried this code:

async routeHandler() {
  sideEffect().catch(console.error)

  await mainEffect();
}

However, when the sideEffect function throws an error, the routeHandler function fails. I can try next, but it does not satisfies the behavior 2.

async routeHandler() {
  try {
    await sideEffect();
  } catch (error) {
    console.error(error);
  }

  await mainEffect();
}

Try catch wrapper Express JS Typescript

I wrote a try catch wrapper for all my routes in Express JS but it doesn’t seem to work as intended.

catchErrors.ts

import { NextFunction, Request, Response } from "express"

export const catchErrors = (originalFunction: any) => {
  return function (req: Request, res: Response, next: NextFunction) {
    try {
      return originalFunction(req, res, next)
    } catch (e) {
      next(e)
    }
  }
}

Then I wrap any route using the wrapper:

router.get('/', catchErrors(this.users))

Got any idea why?

DIFFICULT: Download functionality using streams for large files in NodeJS

I am trying to implement a download functionality using streams in NodeJS.

In the code I am trying to simulate an endpoint that sends data in chunks, something similar to paginated data, for example chunks in size of 5000. Or to make it further clear, we can send top and skip parameters to the endpoint to get a particular chunk of data. If no parameters are provided, it send the first 5000 entries.

There are 2 cases that I am trying to take care of:

  • When the user cancels the download from the browser, how do I handle the continuous fetching of data from the endpoint
  • When the user pauses the download from the browser, how do I pause the data fetching, and then resume once user resumes it

The first case can be taken care of using 'close' event of request. When the connection between the client and the server get cancelled, I disconnect.

If anyone has a better way of implementing this please suggest.

I am having trouble handling the second case when the user pauses.

If anyone could guide me through this, or even provide a better solution to the overall problem(incl. handling the chunks of data), it would be really helpful.

const {createServer} = require('http');
const {Transform} = require('stream');
const axios = require('axios');

class ApiStream extends Transform {
    constructor(apiCallback, res, req) {
        super();
        this.apiCallback = apiCallback;
        this.isPipeSetup = false;
        this.res = res;
        this.req = req
    }
    //Will get data continuously 
    async start() {
        this.res.on('upipie', ()=> console.log("unpipe"))
        let response;
        try {
            response = await this.apiCallback();
        } catch (e) {
            response = null;
        }
        if (!this.isPipeSetup) {
            this.pipe(this.res);
            this.isPipeSetup = true;
        }
        if (response) {
            response = response.data
            if (Array.isArray(response)) {
                response.forEach((item) => {
                    this.push(JSON.stringify(item) + "n");
                });
            } else if (typeof response === "object") {
                this.push(JSON.stringify(response) + "n");
            } else if (typeof response === "string") {
                this.push(response + "n");
            }
            this.start()
        }else{
            this.push(null);
            console.log('Stream ended')
        }
    }
}

const server = createServer(async (req, res, stream) => {
    res.setHeader("Content-disposition", "attachment; filename=download.json");
    res.setHeader("Content-type", "text/plain");
    let disconnected = false;
    const filestream = new ApiStream(async () => {
        let response;
        try {
            if(disconnected){
                console.log('Client connection closed')
                return null;
            }

            response = await axios.get("https://jsonplaceholder.typicode.com/users");

            //Simulate delay in data fetching
            let z = 0;
            if(c>=200) response = null;
            while(z<10000){
                let b = 0;
                while(b<10000){
                    b+=0.5;
                }
                z +=0.5;
            }
        } catch (error) {
            res.status(500).send(error);
        }
        if (response) {
            return response;
        }
        return null;
    }, res, req);
    await filestream.start();

    req.on('close', (err) => {
        disconnected = true;
    })
})
server.listen(5050, () => console.log('server running on port 5050'));

Typescript reducer’s switch case typeguard doesn’t work with object spread

I have a reducer that does different actions depending on the action.type, actions payload is different for certain actions.

    export enum ActionType {
      UpdateEntireState = "UPDATE_ENTIRE_STATE",
      UpdateStateItem = "UPDATE_STATE_ITEM"
    }
    
    type TypeEditData = {
      id: string;
      name: string;
      surname: string;
      age: number;
    };
    
    export type State = TypeEditData[];
    export type Action = UpdateEntireState | UpdateStateItem;
    
    type UpdateEntireState = {
      type: ActionType.UpdateEntireState;
      payload: State;
    };
    
    type UpdateStateItem = {
      type: ActionType.UpdateStateItem;
      payload: { id: string; data: TypeEditData };
    };
    
    export function reducer(state: State, action: Action): State {
      const { type, payload } = action;
    
      switch (type) {
        case ActionType.UpdateEntireState: {
          return [...payload];
        }
        case ActionType.UpdateStateItem: {
          const person = state.filter((item) => item.id === payload.id);
          return [...state, ...payload];
        }
        default: {
          throw Error("Wrong type of action!");
        }
      }
    }

This code won’t work, the errors will say that my action payload can be State or { id: string; data: TypeEditData }.
However, if I access the payload property inside switch case using dot notation like so

return [...action.payload];

There won’t be any errors and the type guard will work fine.
How const { type, payload } = action; differs from action.type and action.payload in terms of types and why doesn’t typeguard work with spread syntax?

TS version – 4.3.4

How can I run SpeechRecognition correctly in chrome

It seems that SpeechRecognition.start() and SpeechRecognition.stop() is working correctly in my chrome.However,SpeechRecognition.onresult() can not be triggered.Please tell my the reason.Thanks a lot.
enter image description here

document.body.onclick = function() {
  recognition.start();
  console.log('Ready to receive a color command.');
}

recognition.onresult = function(event) {
  var color = event.results[0][0].transcript;
  diagnostic.textContent = 'Result received: ' + color + '.';
  bg.style.backgroundColor = color;
  console.log('Confidence: ' + event.results[0][0].confidence);
}

recognition.onspeechend = function() {
  recognition.stop();
  console.log('Command Stop');
}

Nested forEach, both for pushing data in array, but the other one is showing 0 length in console

async function GetUserRecords(dbUID){
const dbUserRef = collection(db, "profile");
const dbUserResRef = collection(db, "profile", dbUID, "records");
const q = query(dbUserResRef, orderBy('date', 'desc'), limit(1));

onSnapshot (q, (querySnapshot2)=>{
    // for (let doc2 of querySnapshot2){
    querySnapshot2.forEach(doc2 =>{
        const data1 = doc2.data();
        usersRecords.push(doc2.data());
    })
});}



async function GetAllDataRealtime(){
const dbUserRef = collection(db, "profile");

 onSnapshot (dbUserRef, (querySnapshot)=>{
     users = [];
     usersRecords = [];
     querySnapshot.forEach(doc =>{

         var dbUID = doc.data().id;
         GetUserRecords(dbUID);
         users.push(doc.data());
     });
     AddAllItemsToTable(users, usersRecords)
})



window.onload = GetAllDataRealtime();

im using this code to get data in firestore and output it in a table, but when I run it, one of the two arrays will have a data inside but the length of it is 0, so it makes it never shows data in my table

How to replace or update query param in route URL?

I want to replace or update the second param of a URL.

My URL is this way edit/id/test-page/id2. I want to replace/update the value of id2 when a button is clicked. I tried the following but few of them are adding new query params to the existing URL, and one example was appending the value to id param.

  1. this.activatedoute.navigate([ this.route.url ], { queryParams: {id: this.id1, id2: this.id2} })

  2. this.activatedoute.navigateByUrl(this.route.url);

Any help is appreciated

Using Vue.js 3 CDN – Uncaught SyntaxError: Cannot use import statement outside a module

I’m using a CDN to play around with Vue 3.

From my index.html, I’m importing my app.js file like this –

<script src="app.js"></script>

The app.js file contains this code –

import createApp  from 'vue'
import Root from './App.vue'

createApp(Root)
    .mount('#app')

I can eliminate this error by adding type="module" to the script tag which will then give me another error involving a MIME type –

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.

Why is this happening and how can I fix this?

Reconnect to group with SignalR

I have a chat application with SignalR in Net5 and @microsoft/signalr in Angular12. The problem I have is when the connection is lost, i need to reconnect and rejoin a group, the reconnection does work but the reconnection to a group does not.

The function I use to join a group in signalr.service.ts is:

 addTogroup(room: any,user:any)

The method I use to recover the connection in signalr.service.ts is:

.withAutomaticReconnect({
    nextRetryDelayInMilliseconds: retryContext => {
      if (retryContext.elapsedMilliseconds < 60000) {
        // If we've been reconnecting for less than 60 seconds so far,
        // wait between 0 and 10 seconds before the next reconnect attempt.

        return Math.random() * 10000;
      } else {
        // If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
        return null;
      }
    }
  })

Is it possible to access in any way the function that joins you to a group, when the withAutomaticReconnect is being executed? To join the group you need the user and the room that are in app.component.ts

Thank you very much for the help, I have already read the documentation many times but I cannot find the solution.

My code is:

app.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { SignalrService } from '../services/signalr.service';
import { HttpClient } from '@angular/common/http';
import { i18nMetaToJSDoc } from '@angular/compiler/src/render3/view/i18n/meta';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @ViewChild('myinputfile') myinputfile: any;

  title = 'chat-ui';
  listMessages: any[] = [];
  messages: string[] = [];
  message: string = "";
  user: string = "";
  ticket: string = "";
  currentFiles: any = [];
  myimg: any = "";

  constructor(public signalrService: SignalrService, private http: HttpClient) {

  }

  ngOnInit(): void {
    this.signalrService.startConnection((message: any) => {
      this.listMessages.push({ mensaje: message.mensaje, user: message.user, tipo: "texto" });
    }, (imageMessage: any) => {
      this.listMessages.push(imageMessage);
    }, (incomingConnection: any) => {
      console.log(incomingConnection);
    }, (error:any) => {
      console.log(error);
    });
  }

  addRoom() {
    this.signalrService.addTogroup(this.ticket, this.user);
  }

  async sendMessage() {
    if (this.myinputfile.nativeElement.files.length > 0) {
      let formData = new FormData();

      formData.append("RoomId", this.ticket);
      formData.append("IdUsuario", this.user);
      formData.append("IdCliente", "1");
      formData.append("Interno", "1");
      formData.append("File", this.myinputfile.nativeElement.files[0]);

      await this.signalrService.sendImagesMessage(formData, (response: any) => {
        this.myimg = response;
      }, (error: any) => {
        console.log(error);
      });

      this.myinputfile.nativeElement.value = "";
    } else {
      this.signalrService.sendMessageGroup(this.ticket, this.user, this.message);
    }
  }

  onaddremoveFiles() {
    if (this.myinputfile.nativeElement.files.length == 0) {
      this.myinputfile.nativeElement.click();
    } else {
      this.myinputfile.nativeElement.value = "";
    }
  }

  onfilesSelected(files: any) { return files.length > 0; }

  openImages(src: any) {
    let data = src;
    let w = window.open('about:blank');
    let image = new Image();
    image.src = data;

    if (w !== null) {
      w.document.write(image.outerHTML);
    }
  }
}

signalr.service.ts

import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr";
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class SignalrService {
  public hubConnection!: signalR.HubConnection;

  constructor(private http:HttpClient) {
    this.buildConnection();
    //this.startConnection();
   }

  public buildConnection = () => {
    this.hubConnection = new signalR.HubConnectionBuilder()
      .configureLogging(signalR.LogLevel.Information)
      .withUrl("https://localhost:44352/chatHub",{
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
      })
      //.withAutomaticReconnect([0,1000,5000,6000,7000,8000,10000,15000])
      .withAutomaticReconnect({
        nextRetryDelayInMilliseconds: retryContext => {
            if (retryContext.elapsedMilliseconds < 60000) {
                // If we've been reconnecting for less than 60 seconds so far,
                // wait between 0 and 10 seconds before the next reconnect attempt.

                return Math.random() * 10000;
            } else {
                // If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
                return null;
            }
        }
    })
      .build();
  };

  public startConnection = (
    onMessageCallback:Function,
    onMessageImageCallback:Function, 
    onIncomingConnectionCallback:Function,
    onErrorCallback:Function) => {
    this.hubConnection
      .start()
      .then(() => {
        console.log("Connection Started...");
        this.ListeningConnections();
        this.ListeningIncomeMessages(onMessageCallback);
        this.ListeningIncomeImagesMessages(onMessageImageCallback);
        this.ListeningIncomingConnection(onIncomingConnectionCallback);
        this.ListeningError(onErrorCallback);
      })
      .catch(err => {
        /*console.log("Error while starting connection: " + err);
        setTimeout(() => {
          this.startConnection();
        }, 3000);*/
      });
  };

  private ListeningConnections(){
    this.hubConnection.on("ReceiveConnID", function (connid) {
      console.log("ConnID: " + connid);
    });
  }
  
  public addTogroup(room: any,user:any){
    this.hubConnection.invoke("AddToGroup", room, user);
  }

  public sendMessageGroup(room: any,user:any,message:any){
    this.hubConnection.invoke("SendMessageGroup",room, user,message,0)
    .catch(function (err) {
      return console.error(err.toString());
    });     
  }

  public ListeningIncomeMessages(onMessageCallback:Function){
    this.hubConnection.on("ReceiveMessageGroup",  (user, message) => {    
      onMessageCallback({mensaje: message, user: user});
    });  
  }

  public ListeningIncomingConnection(onIncomingConnectionCallback:Function){
    this.hubConnection.on("IncomingConnection",(message) => {    
      onIncomingConnectionCallback({mensaje: message});
    });  
  }

  public ListeningUserConnected(onMessageCallback:Function){
    this.hubConnection.on("ReceiveMessageUserConnected",  (user, message) => {    
      onMessageCallback({mensaje: message, user: user});
    });  
  }

  public ListeningError(onErrorCallback:Function){
    this.hubConnection.on("onError", (message) => {    
      onErrorCallback({mensaje: message});
    });  
  }

  public ListeningIncomeImagesMessages(onImageMessageCallback:Function){
    this.hubConnection.on("ReceiveImageMessageGroup", (user, image) => {
      //console.log(image);
     // console.log(image.idUsuario);
     // console.log(image.idCliente);

      onImageMessageCallback({ mensaje: image, user: user, tipo: "imagen" });
    });
  }

  public async sendImagesMessage(formData:FormData,onOkCallback:Function,onErroCallback:Function){
    await this.http.post("https://localhost:44352/UploadImages",formData).toPromise().then((response)=>{
      onOkCallback(response);
    }).catch((error)=>{
      onErroCallback(error);
      console.log(error);
    });
  }  
}

ChatHub.cs

    public class ChatHub : Hub
    {
        public readonly static List<UserViewModel> _Connections = new List<UserViewModel>();
        public readonly static List<RoomViewModel> _Rooms = new List<RoomViewModel>();
        private readonly static Dictionary<string, string> _ConnectionsMap = new Dictionary<string, string>();

        private readonly TicketingContext _context;
        private readonly IMapper _mapper;

        private readonly IMensajeService _mensajeService;
        private readonly IUnitOfWork _unitOfWork;
        public ChatHub(TicketingContext context, IUnitOfWork unitOfWork, IMensajeService mensajeService, IMapper mapper)
        {
            _context = context;
            _mensajeService = mensajeService;
            _unitOfWork = unitOfWork;
            _mapper = mapper;
        }

        public IEnumerable<RoomViewModel> GetRooms()
        {
            if (_Rooms.Count == 0)
            {
                foreach (var room in _context.Tickets)
                {
                    var roomViewModel = _mapper.Map<Tickets, RoomViewModel>(room);
                    _Rooms.Add(roomViewModel);
                }
            }
            return _Rooms.ToList();
        }

        public IEnumerable<UserViewModel> GetUsers(string roomName)
        {
            return _Connections.Where(u => u.CurrentRoom == roomName).ToList();
        }

        //envia mensajes a un grupo determinado
        public async Task SendMessageGroup(string room, string user, string message, int interno)
        {
            try
            {
                var msg = new MensajeGrabarDto()
                {
                    IdTicket = Int32.Parse(room),
                    IdUsuario = Int32.Parse(user),
                    Texto = Regex.Replace(message, @"(?i)<(?!img|a|/a|/img).*?>", string.Empty),
                    Interno = interno,
                    Adjunto = new byte[] { },
                    AdjuntoNombre = "",
                    AdjuntoTipo = ""
                    //llenar los campos
                };

                DbResponse respon = await _unitOfWork.MensajeRepository.InsertarMensajeRepository(msg);           

                if (respon.retcode == 0)
                {
                    await _unitOfWork.SaveChangesAsync();

                    var mensaje = await _mensajeService.ObtenerMensajesPorTicketService(Int32.Parse(room));

                    var mensaje2 = mensaje.Where(x => x.IdTicket == Int32.Parse(room)).OrderByDescending(x => x.IdMensaje).FirstOrDefault();

                    await Clients.Group(room).SendAsync("ReceiveMessageGroup", user, mensaje2);
                }
                else {
                    await Clients.Caller.SendAsync("onError", respon.mensaje);
                }
            }
            catch (Exception)
            {
                await Clients.Caller.SendAsync("onError", "Message not send! Message should be 1-1500 characters.");
            }

        }

        //une a un usuario a determinado grupo
        public async Task AddToGroup(string room, string usuario) 
        {
            try
            {
                await Groups.AddToGroupAsync(Context.ConnectionId, room);

                //mensaje para decir que alguien se conecto
                await Clients.Group(room).SendAsync("IncomingConnection", $"Alguien se conecto { usuario}");
            }
            catch (Exception ex)
            {
                await Clients.Caller.SendAsync("onError", "You failed to join the chat room!" + ex.Message);
            }            
        }

        //desloguea a un usuario de un grupo
        public async Task Leave(string roomName)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
        }

        public override Task OnConnectedAsync()
        {
            try
            {
                Clients.Client(Context.ConnectionId).SendAsync("ReceiveConnID", Context.User.Identity.Name);
            }
            catch (Exception ex)
            {
                Clients.Caller.SendAsync("onError", "OnConnected:" + ex.Message);
            }
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            try
            {

            }
            catch (Exception ex)
            {
                Clients.Caller.SendAsync("onError", "OnDisconnected: " + ex.Message);
            }

            return base.OnDisconnectedAsync(exception);
        }
    }