.NET Angular how to get data asynchronously - javascript

First I'm trying to get all projects from the database by given userId from URL. I'm doing it in ngOnInit(). Each project has it's field Languages which is a list of objects from another table Languages. One record in this table has a field named projectId by which I'm trying to get all languages for each project. I think I have a problem with receiving data asynchronously because projects are received properly but languages field in each Project object is null. What should I do?
ngOnInit() {
this.userId = this.route.snapshot.params['id'];
this.getAllProjectsByUserId(this.userId);
this.getLanguagesByProjectId();
}
getAllProjectsByUserId(id: number) { //receivng all projects for given userId
this.portfolioAppService.getAllProjectsByUserId(id).subscribe((data) => {
this.projectsList = data;
console.log(this.projectsList);
},
error => console.error(error)
);
}
getLanguagesByProjectId() { //receiving all languages for each project
this.projectsList.forEach(x => {
this.portfolioAppService.getLanguagesByProjectId(x.id).subscribe((data) => {
x.languages = data;
console.log(this.projectsList);
},
error => console.error(error)
);
});
}

Move the call to getLanguagesByProjectId() to when the projects have been received:
getAllProjectsByUserId(id: number) { //receiving all projects for given userId
this.portfolioAppService.getAllProjectsByUserId(id).subscribe((data) => {
this.projectsList = data;
console.log(this.projectsList);
this.getLanguagesByProjectId();
},
error => console.error(error)
);
}
As otherwise this.projectsList property has not the needed values yet.

Related

Why are my messages from BehivourSubject duplicated every new recieved message?

I am using BehivourSubject for recieve data, also using websocket but websocket is not important for now. For now is very important why I always got duplicated message from BehivourSubject.
Check code in service:
hubMessage$ = new BehaviorSubject({});
public startConnection = (id: number) => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://api/hub')
.build();
this.hubConnection
.start()
.then(() => {
console.log('connection established')
}
)
.catch(err => {
console.log('Error while starting connection: ' + err)
this.retryConnection();
})
}
public newLocationRecieved() {
this.hubConnection.on('NewLoc', (data) => {
console.log('new location recieved' , data)
this.hubMessage$.next(data);
})
}
public sendDriverId(id: number = 1) {
this.hubConnection.send('SubOnDriver', { driverId: id })
}
Every 20 seconds i got new location. I need to remove previous data from this hubMessage or what ever but my software crash after one minute because duplicated message.
Imagine that your messages are duplicated. To get one first, then two, then four, then sixty..32....64..
Why?
How to solve this ?
I am using this message in component:
Component code:
ngOnChanges() {
this.dispatchDetails;
this.createMarkers();
}
createMarkers() {
console.log('Connection start right now ', this.dispatchDetails)
this.newCoordinate();
}
private newCoordinate = () => {
this.signalRService.hubMessage$.subscribe(
(data: any) => {
console.log('recieved new coordinate ?', data); // HERE I GOT SO MUCH DUPLICATED MESSAGE
this.signalRService.newLocationRecieved()
this.locationCoords = data;
if (this.locationCoords.location) {
this.latitude = this.locationCoords?.location?.latitude
this.longitude = this.locationCoords?.location?.longitude
}
}
)
}
I probably need to clear my variable...
Or maybe is problem with websocket connection ?
is the websocket connection duplicated? Which I don't believe
you probably know that I can't make minimal reproduction code because these are web sockets ...
This is because you are calling createMarkers() function from ngOnChanges life cycle. ngOnChanges will be called again and again when there is component scoped value change. So, new subscriptions will be created again and again, so you have bunch of duplicated messages. You need to call that function only when you create a component or whatever only one time. Or you need to kill existing subscription first before creating a new subscription.
You've subscribed to this.signalRService.hubMessage$ and on any new data you receive from that behavior subject you call this.signalRService.newLocationRecieved()
this.signalRService.hubMessage$.subscribe(
(data: any) => {
...
this.signalRService.newLocationRecieved()
}
which in turn creates a brand new callback
public newLocationRecieved() {
this.hubConnection.on('NewLoc', (data) => {
this.hubMessage$.next(data);
})
}
hence your duplicated values. What I would suggest is moving the callback into the startConnection function
public startConnection = (id: number) => {
...
this.hubConnection.on('NewLoc', (data) => {
console.log('new location recieved', data)
this.hubMessage$.next(data);
});
}
Edit:
Also this.createMarkers(); should be moved out of ngOnChanges into ngOnInit. All credit to #Liu Zhang
In the message handler inside newCoordinate, you call this.signalRService.newLocationRecieved() , which sets up another event handler that publishes another message into the same hubMessage$. You want to do this only once, probably in startConnection.

Adding and appending value to an array with Realtime Database

I'm making a React.js app's where the user can upload a song to firebase and then he will see the queque with all the uploaded songs in order. Queque can be sorted with a drag and drop system, that will be update the database in Firebase. When uploading a songs, is there a way to insert that songs in an array? The schema will be:
project-name:
[0]:
- name
- artist
- duration
[1]:
- name
- artist
- duration
[ecc]
How can I add an array and then append items in the database?
I'm using nodejs for backend and socket.io to send new sorted queque to node.
uploadSongs.js:
queueRef.set()
queueRef.update({
...metas,
path: fileName,
}, (error) => {
if (error) {
console.log("Data could not be saved." + error)
} else {
console.log("Data saved successfully.")
}
})
songs.js:
io.on('connect', socket => {
var quequeRef = admin.database().ref()
quequeRef.on('value', function (snapshot) {
var queque = snapshot.val()
if (queque != null) {
var value = Object.values(queque)
io.emit('queueSongs', Object.values(value))
} else {
io.emit('queueSongs', [])
}
})
socket.on('queueSongs', (songs) => {
console.log(songs)
quequeRef.set(songs, (error) => {
if (error) {
console.log("Data could not be saved." + error)
} else {
console.log("Data saved successfully.")
}
})
})
})
Thanks
If I understand you correctly this is how I would do it.
You have an collection called que inside of your collection que you have documents.
when you add your information to your document you may want to add an key called orderId.
so this is how your document will look
{
orderId:0,
artist:'haddaway',
title:'baby dont hurt me'
}
So when you are fetching your data you do something like
db.collection('que').orderBy('orderId').get()
That way you will always get the order that you put them in.

Angular, mysql and nodejs create a listview

i'm new to angular, the journey is going quite well for now but it's also a bit challenging. Managed to fix some issues with the application like insert dating successfully in the server and so on, but trying to retrieve it inside a listview in angular seems like its not working. Please assist
I am struggling to create a listview using data from nodejs in mysql.
angular is not giving me the data.
index.html:
<div class="message-list">
<ul class="message-thread">
<li ng-repeat="ppPack in data.pp"
>
{{data.pp}} Posted by {{data.username}}
</li>
</ul>
</div>
controller
$scope.selectFeed = () => {
appService.getFeedList().then( (response) => {
$scope.$apply(() => {
let ppPack = {
fromUserId: document.querySelector(" from_user_id").value,
post: document.querySelector("#post").value
}
});
$scope.data.pp = response.pp;
}).catch( (error) => {
console.log(error);
alert('Unexpected Error, Try go back and redo somethings you did!.');
});
}
helper class
async getFeedList(){
try {
return await this.db.query(
`SELECT * FROM posts ORDER BY id ASC`,
[params.from_user_id,params.posts]
);
} catch (error) {
console.warn(error);
return null;
}
}
service class
getFeedList() {
return new Promise((resolve, reject) => {
this.httpCall({
url: '/getPosts',
'posts': $scope.data.pp,
'from_user_id': $scope.data.username
}).then((response) => {
resolve(response);
}).catch((error) => {
reject(error);
});
});
}
ng-repeat uses the value that you pass to the dom.
Starting with your helper class, you need to pass out the value in which you want to collect from the db, even though it seems like you query everything, giving params value to them will make it easier for you to understand the logic of the code, which means you query should be:
async getFeedList(posts, from_user_id)){
try {
return await this.db.query(
`SELECT * FROM posts ORDER BY id ASC`,
[params.from_user_id,params.posts]
);
} catch (error) {
console.warn(error);
return null;
}
}
And in your service class, also pass the params
getFeedList(posts, from_user_id) {
return new Promise((resolve, reject) => {
this.httpCall({
url: '/getPosts',
'posts': posts,
'from_user_id': from_user_id
}).then((response) => {
resolve(response);
}).catch((error) => {
reject(error);
});
});
}
and in your controller file should be outside other scopes since they won't be called out unless they requesting the service on their own like this:
appService.getFeedList()
.then((response) => {
$scope.$apply(() => {
$scope.postsData = response.postsData;
//output to see the array before commit to the scope
console.log(response);
});
})
.catch((error) => {
console.log(error);
alert("error code!.");
});
and then finally in your home.html you can call
<div class="list-group">
<ul class="list-group-item"
ng-repeat="post in postsData">
{{post.post}} Posted by <strong>{{post.from_user_id}} </strong</ul>
</div>
Some best practise tips from the official angular site:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.

How to create/ update large quantity records in azure cosmos database

I have 1500 records to be created in azure cosmos database, I just loop through using Javascript code. I have REST API connection to the database. I feed the data to be updated as JSON array.
The problem is when I pass entire data, azure database timesout or send ECONNECTIONRESET. Many of you would create huge amount of records in the database and there is might be some efficient way to overcome this problem. I want your suggestion.
Since majority of records is not found, many times create new record part is hit and I have never pushed such huge amount of data before. Any suggestion or new idea will really help me.
Note : I run this javascript code using mocha unit test
Below is code snippet
Record.js
const fetch = require('node-fetch');
let connectionAzureDataBase = "abc...";
let SubscriptionKey = "xyz";
let promises = [];
let j = -1;
module.exports = {
checkRecord
}
function checkRecord (req) {
for (let i = 0; i < req.body.length; i++) {
promises[j] = new Promise(async function(resolve, reject) {
//check if record exist in azure
var apiUrl = APICheckRecord( req.body[i].recordName);
fetch(apiUrl , { headers:connectionAzureDataBase})
.then(res => res.json())
.then(record => {
if(record) {
console.log("Record Found");
} else {
console.log("Record not Found, calling API to create Record");
var apiUrl = APICreateNewRecord( req.body[i].recordName);
fetch(apiUrl , { headers:connectionAzureDataBase})
.then(res => res.json())
.then(recordCreated => {
if(recordCreated) {
console.log("record created successfully");
resolve("record created successfully");
} else {
console.log("Encountered some unexpected condition");
resolve("Encountered some unexpected condition");
}
}).catch(err => {
console.log("record could not be created");
resolve("record could not be created");
})
}
}).catch(err => {
console.log("record not found");
resolve("record not found");
})
})// close promise
}// close for
let replies = await Promise.all(promises);
let promise1 = new Promise (function(resolve,reject) {
resolve(replies);
})
}
Record.spec.js
const Records = require("Record.js);
it("should find/create records", async function() {
this.timeout(6000000);
try {
let req =[
{
"recordName": "Xyz",
"recordDate": "12-06-2020"
},
{
"recordName": "Abc",
"recordDate": "13-06-2020"
}
]
let reply = await Records.checkRecord(req);
console.log(JSON.stringify(reply));
} catch(err) {
console.log(err);
}
})
Error
message: 'request to https://apim-dev.azure-api.net/api/portal/records/?recordName="Xyz" failed, reason: read ECONNRESET',
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET' }
message: 'request to https://apim-dev.azure-api.net/api/portal/createRecords/ failed, reason: read ECONNRESET',
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET' }
This is sample of data that is passed, I have 1500 such records
SampleData
[
{
"recordName": "Xyz",
"recordDate": "12-06-2020"
},
{
"recordName": "Abc",
"recordDate": "13-06-2020"
}
]
You can use Azure Cosmos DB Data Migration tool, which can import data from various sources into Azure Cosmos containers. You can import from JSON files. You migrate that data to collections for use with Azure Cosmos DB. The Data Migration tool can also be used when migrating from a single partition collection to a multi-partition collection for the SQL API.
Import JSON files
The JSON file source importer option allows you to import one or more single document JSON files or JSON files that each have an array of JSON documents. When adding folders that have JSON files to import, you have the option of recursively searching for files in subfolders.

Angular 6: Adding data to database using ID

I have an app which displays just movies from external api, so I have added comment section to my app for single movie, my problem is when a user enter a comment in a movie called "TITANIC" I can see the comment added to all movies This is wrong, what I want is comment added to a movie TITANIC should not be displayed to other movies.
Here is what I have:
server.js UPDATE
router.post('/comments', function(req, res) {
var comment = new Comments(req.body);
comment.save()
.then(item => {
res.status(200).json({'comment': 'comment added successfully'});
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
service.js
// Adds comments
addReview(author, description): Observable<any> {
const uri = 'http://localhost:8000/movies/comments/';
const obj = {
author: author,
description: description
};
return this.http.post(uri, obj);
}
// Return Comments
getComments(id: string): Observable<any> {
const url = `${apiUrl + this.commentsUrl}/${id}`;
return this.http.get(url, httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
component.ts
addReview(author, description) {
this.moviesService.addReview(author, description).subscribe(success => {
this.flashMessages.show('You are data we succesfully submitted', { cssClass: 'alert-success', timeout: 3000 });
// get the id
this.activeRouter.params.subscribe((params) => {
// tslint:disable-next-line:prefer-const
let id = params['id'];
this.moviesService.getComments(id)
.subscribe(comments => {
console.log(comments);
this.comments = comments;
});
});
}, error => {
this.flashMessages.show('Something went wrong', { cssClass: 'alert-danger', timeout: 3000 });
});
}
Question
What am I missing in my code?
You should /post to a specific movie ID where you add the new comment under its comments field array for example.
Also, having /comments is a bad practice since what you really want is posting to a specific movie with its attached comment. Something like the following:
router.post('/movie/:id/newcomment:', function(req, res) {
Movie.findOne({ _id: req.params.id })
.then(movie => {
movie.comments.push(req.body.comment);
movie.save();
.then(result => {
res.status(200).json({'message': 'comment added successfully'});})
.catch(err => {
res.status(400).send("unable to save to database");
});
}
});
But the main problem here is that you misunderstand what exactly you need to do.
You fetch data from an existing API, yet you want to add specific comments to specific movies from the fetched data.
You cannot achieve it without having full control on your external API.
What you could actually do is build a client side data structure to hold all the fetched data, and then add to each movie object inside that data structure another property which will be named comments and an ID property, so you can later access that data structure by ID and update a specific property.
1) Add the corresponding movie id to the POST request adding a new comment.
addReview(movieId, author, description): Observable<any> {
const obj = {movieId, author, description};
2) When you request the comments for a movie by its id, use the movieId in your database query (depends on your database and ORM)
If you're using mongodb and mongoose, it'd be something like:
router.get('/comments/:movieId', function(req, res) {
const movieId= request.params.movieId;
Comments.find({ movieId });

Categories

Resources