In my Angular app I am trying to save image and text inside a table. Everything is working fine. I can add the the data, I can get the data. But the problem is if I click on save, data shows inside the table but only texts can be seen without refresh, image is showing the alt image value.
But if I refresh the page the page it works perfectly.
and in the table I can see something like this,
Here is my service file:
#Injectable({
providedIn: 'root'
})
export class CategoriesService {
private categories:Category[] = [];
private categoryUpdated = new Subject<Category[]>();
constructor(private http : HttpClient, private router: Router) { }
getUpdateListener(){
return this.categoryUpdated.asObservable();
}
/* posting request */
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData.category.id,
name : name,
image : responseData.category.image
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}
/* getting categories, data must be as backend i.e message and object */
getCategories(){
this.http.get<{message: string; categories: any}>(
"http://localhost:3000/api/v1.0/categories"
)
.pipe(map((cateData)=>{
return cateData.categories.map(category=>{
return {
id: category._id,
name : category.name,
image: category.image
}
})
}))
.subscribe(transformedCate =>{
this.categories = transformedCate;
this.categoryUpdated.next([...this.categories])
})
}
}
And my main component.ts file:
export class CategoriesComponent implements OnInit,OnDestroy{
togglePanel: any = {};
categoryPanel: any = {};
categories : Category[] = [];
private categorySub : Subscription;
constructor(private _categoriesService : CategoriesService, private dialog : MatDialog){}
ngOnInit(){
this._categoriesService.getCategories();
this.categorySub = this._categoriesService.getUpdateListener().subscribe((cate: Category[])=>{
this.categories = cate;
})
}
OnFormOpen(){
this.dialog.open(CategoryFormComponent)
}
ngOnDestroy(){
this.categorySub.unsubscribe();
}
}
And my form component:
export class CategoryFormComponent implements OnInit {
form : FormGroup;
imagePreview : string;
constructor(private dialogRef : MatDialogRef<CategoryFormComponent>,
#Inject (MAT_DIALOG_DATA) private data : any,
private _categoriesService : CategoriesService) {}
onCancel(){
this.dialogRef.close();
}
ngOnInit(): void {
this.form = new FormGroup({
name : new FormControl(null,{validators:[Validators.required, Validators.minLength(3)]}),
image : new FormControl(null,{validators: [Validators.required], asyncValidators : [mimeType]})
})
}
/*event for checking the image after load */
onImgPicked(event : Event){
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({image: file});
this.form.get('image').updateValueAndValidity();
// console.log(file);
// console.log(this.form)
const reader = new FileReader();
reader.onload = () =>{
this.imagePreview = reader.result as string;
};
reader.readAsDataURL(file);
}
/*On category added */
OnCategoryAdded(){
//is loading
this._categoriesService.addCategory(this.form.value.name, this.form.value.image);
this.form.reset();
this.dialogRef.close();
}
}
Setting a timeout on ngOnInit works but I want to make it without settiimeout
setTimeout(() => {
this.OnInit();
},10000)
}
It looks you are misunderstanding the properties of the http response in addCategory. Try modifying as below.
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData._doc.id, // here
name : name,
image : responseData._doc.image // here
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}
Related
I have a code written in node-nats-streaming and trying to convert it to newer jetstream. A part of code looks like this:
import { Message, Stan } from 'node-nats-streaming';
import { Subjects } from './subjects';
interface Event {
subject: Subjects;
data: any;
}
export abstract class Listener<T extends Event> {
abstract subject: T['subject'];
abstract queueGroupName: string;
abstract onMessage(data: T['data'], msg: Message): void;
private client: Stan;
protected ackWait = 5 * 1000;
constructor(client: Stan) {
this.client = client;
}
subscriptionOptions() {
return this.client
.subscriptionOptions()
.setDeliverAllAvailable()
.setManualAckMode(true)
.setAckWait(this.ackWait)
.setDurableName(this.queueGroupName);
}
listen() {
const subscription = this.client.subscribe(
this.subject,
this.queueGroupName,
this.subscriptionOptions()
);
subscription.on('message', (msg: Message) => {
console.log(`Message received: ${this.subject} / ${this.queueGroupName}`);
const parsedData = this.parseMessage(msg);
this.onMessage(parsedData, msg);
});
}
parseMessage(msg: Message) {
const data = msg.getData();
return typeof data === 'string'
? JSON.parse(data)
: JSON.parse(data.toString('utf8'));
}
}
As I searched through the documents it seems I can do something like following:
import { connect } from "nats";
const jsm = await nc.jetstreamManager();
const cfg = {
name: "EVENTS",
subjects: ["events.>"],
};
await jsm.streams.add(cfg);
But it seems there are only name and subject options available. But from my original code I need a data property it can handle JSON objects. Is there a way I can convert this code to a Jetstream code or I should change the logic of the whole application as well?
Basically what I need is generate one single file that contain all vendors and dependencies for each entry..
export default defineConfig({
plugins: [ react() ],
build: {
rollupOptions: {
input: {
popup: path.resolve(pagesDirectory, 'popup', 'index.html'),
background: path.resolve(pagesDirectory, 'background', 'index.ts'),
content_script: path.resolve(pagesDirectory, 'content_script', 'ContentScript.tsx')
},
output: {
entryFileNames: 'src/pages/[name]/index.js',
chunkFileNames: isDevelopment ? 'assets/js/[name].js' : 'assets/js/[name].[hash].js',
assetFileNames: (assetInfo) => {
const { dir, name: _name } = path.parse(assetInfo.name);
const assetFolder = getLastElement(dir.split('/'));
const name = assetFolder + firstUpperCase(_name);
return `assets/[ext]/${name}.chunk.[ext]`;
}
}
}
}})
In this case.. will be one file for popup, background and content_script
Here is one example of ContentScript.tsx file...
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Badge from './badge';
function init(query: string) {
const appContainer = document.querySelector('#search') as HTMLElement;
if (!appContainer) {
throw new Error('Can not find AppContainer');
}
const rootElement = document.createElement('div');
rootElement.setAttribute('id', 'web-answer-content-script');
appContainer.insertBefore(rootElement, appContainer.firstChild);
const root = createRoot(rootElement, {});
root.render(
<React.StrictMode>
<Badge query={query} />
</React.StrictMode>
);
}
const searchParameters = new URLSearchParams(window.location.search);
if (searchParameters.has('q')) {
const query = searchParameters.get('q');
init(query);
}
import MessageSender = chrome.runtime.MessageSender;
function handleMessageReceived(message: string, sender: MessageSender) {
console.log('>>> MESSAGE RECEIVED', message, sender);
}
chrome.runtime.onMessage.addListener(handleMessageReceived);
With this configuration I'm getting this..
and my content_scripts/index.js ..
import { j as n, c as a, r as c } from '../../../assets/jsx-dev-runtime.a077470a.js';
// ..rest of the code...
As you can see.. I don't want this import... statement ..
I have this:
LocationController.ts
import {GenericController} from './_genericController';
interface Response {
id : number,
code: string,
name: string,
type: string,
long: number,
lat: number
}
const fields = ['code','name','type','long','lat'];
class LocationController extends GenericController{
tableName:string = 'location';
fields:Array<any> = fields;
}
const locationController = new LocationController();
const get = async (req, res) => {
await locationController._get(req, res);
}
export {get};
GenericController.ts
interface Response {
id : number
}
export class GenericController{
tableName:string = '';
fields:Array<any> = [];
_get = async (req, res) => {
try{
const id = req.body['id'];
const send = async () => {
const resp : Array<Response> = await db(this.tableName).select(this.fields).where('id', id)
if (resp[0] === undefined) {
// some error handling
}
res.status(status.success).json(resp[0]);
}
await send();
}catch (error){
// some error handling
}
}
}
What I want to do is to pass the Response interface from LocationController to the GenericController parent, so that the response is typed accurately depending on how the child class has defined it. Clearly it doesn't work like this since the interface is defined outside of the class so the parent has no idea about the Response interface in the LocationController.ts file.
I've tried passing interface as an argument in the constructor, that doesn't work. So is there a way I can make this happen? I feel like I'm missing something really simple.
Typically, generics are used in a situation like this. Here's how I'd do it:
interface Response {
id: number;
}
// Note the generic parameter <R extends Response>
export class GenericController<R extends Response> {
tableName: string = "";
fields: Array<any> = [];
_get = async (req, res) => {
try {
const id = req.body["id"];
const send = async () => {
// The array is now properly typed. You don't know the exact type,
// but you do know the constraint - R is some type of `Response`
let resp: Array<R> = await db(this.tableName).select(this.fields).where("id", id);
if (resp[0] === undefined) {
// some error handling
}
res.status(status.success).json(resp[0]);
};
await send();
} catch (error) {
// some error handling
}
};
}
import { GenericController } from "./_genericController";
interface Response {
id: number;
code: string;
name: string;
type: string;
long: number;
lat: number;
}
const fields = ["code", "name", "type", "long", "lat"];
// Here we tell the GenericController exactly what type of Response it's going to get
class LocationController extends GenericController<Response> {
tableName: string = "location";
fields: Array<any> = fields;
}
const locationController = new LocationController();
const get = async (req, res) => {
await locationController._get(req, res);
};
export { get };
If this is not enough and you wish to somehow know the exact response type you're going to get, I believe the only way is a manual check. For example:
import { LocationResponse } from './locationController';
// ... stuff
// Manual runtime type check
if (this.tableName === 'location') {
// Manual cast
resp = resp as Array<LocationResponse>
}
// ...
You could also check the form of resp[0] (if (resp[0].hasOwnProperty('code')) { ... }) and cast accordingly. There are also nicer ways to write this, but the basic idea remains the same.
Generally, a properly written class should be unaware of any classes that inherit from it. Putting child-class-specific logic into your generic controller is a code smell. Though as always, it all depends on a particular situation.
After server rebuild, compiller creates instanse in included api controller here:
NewController.ts
import express = require("express");
import INew = require("../interface/INew");
import NewRepository = require("../repositories/NewRepository");
class NewController {
private _newRepository: INew;
constructor() {
this._newRepository = new NewRepository();
this._newRepository.findById(5);
}
retrieve(req: express.Request, res: express.Response): void {
try {
console.log('-----------retrieve--------------------');
this._newRepository.findById(2);
}
catch (e) {
console.log(e);
}
}
}
Object.seal(NewController);
export = NewController;
constructor works: i see console message:
-------------NewRepository------------------
5 'RESULT'
NewRepository.ts:
import INew = require("../interface/INew");
import bluebird = require("bluebird");
class NewRepository implements INew {
sd: string;
constructor() {
console.log('-------------NewRepository------------------');
}
findById(id: number): void {
setTimeout(function () {
console.log(id, 'RESULT');
}, 3000);
}
}
export = NewRepository;
INew.ts
interface INew {
findById: (id: number) => void;
sd: string;
}
export = INew;
Buut when i use controller's method 'retrieve', visit rout '/new' then i get error [TypeError: Cannot read property '_newRepository' of undefined] instead : 2 'RESULT'
Angular 2 helps me with routing:
.............
getCarsRestful(): Promise<New[]> {
console.log('-------------------------------');
return this.http.get('api/new')
.toPromise()
.then(response => response.json())
.catch(this.handleError);
}
...................
and execute backend:
NewRoutes.ts
import express = require("express");
import NewController = require('../controllers/NewController');
var router = express.Router();
class NewRoutes {
private _newController: NewController;
constructor() {
this._newController = new NewController()
}
get routes() {
var controller = this._newController;
router.get("/new", controller.retrieve);
return router;
}
}
Object.seal(NewRoutes);
export = NewRoutes;
my created instanse '_newRepository' doesn't exist already, why? i get console log:
-----------retrieve--------------------
[TypeError: Cannot read property '_newRepository' of undefined]
Help please, how to make 'singltone' in ts
i don't want to create it in every controller's method, though, that works:
.................
retrieve(req: express.Request, res: express.Response): void {
try {
var _newRepository: INew;
_newRepository = new NewRepository();
_newRepository.findById(2);
.............
Try explicitly set this in router config:
router.get("/new", controller.retrieve.bind(controller));
I am stuck at some point working with angular 2 with node js. below is my code. It couldn't set rows(variable) to this.rowsdata (variable).
I think the reason behind is that node Js uses asynchronous call. May be that's why this.rows data get undefined for rows.
/// <reference path="../typings/node/node.d.ts" />
import { Component } from 'angular2/core';
import { OnInit } from 'angular2/core';
declare var module : any;
declare var jQuery : any;
var rowsdata : any[] = [];
// self = this;
interface ROWS {
name : string,
current_balance : number
}
#Component({
selector : 'portfolioList',
templateUrl : '../app/view/portfoliolist.html',
moduleId : module.id
})
export class PortfolioList implements OnInit {
// self : any = this;
constructor() {
// var self : this;
}
ngOnInit(): any {
this.showdata();
// console.log(this.rowsdata);
}
showdata() {
console.log("called");
var portfolioList = require('../app/api/showPortfolio.js');
portfolioList.showPortfolio(function(err:any, rows:any) {
console.log(rows);
// self.rowsdata = rows;
this.rowsdata = rows;
});
// console.log(self.rowsdata);
console.log(rowsdata);
}
}
here is the showPortfolio function
exports.showPortfolio = function(callback) {
db.all(squel .select()
.from("portfolios")
.field("name")
.field("current_balance") .toString() , function(err, rows) { callback(null, rows) });
}
In your code :
portfolioList.showPortfolio(function(err:any, rows:any) {
Change this to a fat arrow:
portfolioList.showPortfolio((err:any, rows:any) => {
More
https://basarat.gitbooks.io/typescript/content/docs/arrow-functions.html
i cant post a comment with all this code, so i post an answer.
it should work like this. i've put some extra logs.
showdata() {
console.log("called");
var _this = this;
console.log(_this);
var portfolioList = require('../app/api/showPortfolio.js');
portfolioList.showPortfolio(function(err:any, rows:any) {
console.log(rows);
console.log(_this);
_this.rowsdata = rows;
});
// console.log(self.rowsdata);
console.log(rowsdata);
}
}