So I have a variable called stuff, which is declared as stuff:any and I would like to populate it with the data that I get from:
this.http.post("WORKING_IP_ADDRESS", JSON.stringify(
{"thing1": this.thing1,
"thing2": this.thing2,
}), {headers: headers}).map(
res => res.json()).subscribe(
data => {
(JSON.stringify(data));
})
When I run an alert() method on the last JSON.stringify(data));, I get the data that I want, but when I try to set it equal to the stuff variable by doing this.stuff = JSON.stringify(data));, it doesn't work. I'm new to Angular, so I know I'm probably doing something wrong. Anyone know what I need to do to get the final JSON.stringify(data) into my stuff variable?
The reason is that http.post is a observable. That means that its gonna resolve later. It's gonna resolve later because its gonna take some time to connect to that working ip address, get the data and do something with it.
Imagine a scenario like this
let stuff = 'no';
// Equal to http.post
const resolvesLater = setTimeout(function() {
stuff = 'yes';
console.log(stuff);
},2000);
console.log(stuff);
https://jsfiddle.net/7c6kfurp/ Open up the console and press play. See what happens.
You would think that the console would look like
yes
yes
But it actually is gonna be
no
yes
Because what happens in the setTimeout happens later. So the order of the lines don't tell you in what order the code executes.
Its gonna set stuff = 'no' at the beginning.
Then its gonna go into the setTimeout/http.post function
That's gonna take some time.
Meanwhile the browser continues
It logs out stuff (still no)
Then the setTimeout/http.post resolves
Its gonna set stuff = yes
Then log out stuff (which is now yes)
In angular 2 you subscribe to observables (like you do) but it would probably be better to decouple the fetch and subscribe. So create a service with a function:
fetchStuff(): Observable<Stuff[]> {
return his.http.post("WORKING_IP_ADDRESS", JSON.stringify({
"thing1": this.thing1,
"thing2": this.thing2,
}), {headers: headers}).map(res => res.json());
}
And then you subscribe to it and assign the stuff variable for the views
getStuff() {
this.MyService.fetchStuff()
.subscribe(stuff => this.stuff= stuff);
}
And finally in your view
<ul class="items">
<li *ngFor="let item of stuff>
<span>{{item.property}}</span>
</li>
</ul>
Observables means that when the object changes all other objects that are dependant on it are notified.
So you make a request to your working-ip, first nothing happens, like I explained above. We're waiting for the server to respond, so something with the data you sent and then send you something back.
In order to know when something happened, you subscribe to it. Just like you would subscribe a newspaper to get the latest news. When something happens you get notified. You want to know when you get response from the-working-ip.
The reason I broke it up was to seperate the fetching and the subscription, just to seperate the concerns. You don't have to and I've probably confused things more for you. Sorry about.
Simply try this:
this.http.post("WORKING_IP_ADDRESS", {
"thing1": this.thing1,
"thing2": this.thing2
}, {headers: headers})
.map(res => res.json())
.subscribe(data => this.stuff = data);
Here's a link on how to create services
https://angular.io/docs/ts/latest/guide/server-communication.html
Related
Can anyone explain how or why my test isn't waiting for my data from an axios request before moving on? I'm completely new to this but have most simple stuff worked out but can't seem to navigate the docs to find where i'm going wrong.
Here's the relevant info..
cy.get('.day').eq(4).click() //Change the day
cy.route('/api/practice/available-slots').as('apiCheck') //Get available slots for that day
cy.wait('#apiCheck') //Wait for the days available slots to be returned
So you can see below I click the fourth day and my post URL is showing and getting data like it normally does but then my wait function throws that error. I like to think i'm close but as I said i'm new and not entirely sure what's going wrong. Thanks
For what it's worth here's the axios request:
axios
.post(this.props.reqProto + this.props.reqHost + '/api/practice/available-slots', {
startDate: this.state.appointmentSlotsDate,
})
.then((res) => {
....
}
})
Thanks Hiram,
That was one issue. Also the order of my code was incorrect. I need to allow cypress to anticipate the POST request instead of it trying to double back to it. This seems to work
cy.route({
method: 'POST',
url: '/api/practice/available-slots',
}).as('apiCheck')
cy.get('.day').eq(4).click()
cy.wait('#apiCheck')
Here I'm trying to push the data inside the array in Firebase, but it's pushing the data continuously until the cache from the app is destroyed. Here is my code and Firebase screenshot.
code:
var Input = {
AaMessage: 'brb',
}
var query = firebase.database().ref('UserList/');
query
.orderByChild('PostId')
.equalTo(this.state.PostID)
.on('value', snapshot => {
snapshot.forEach(child => {
firebase
.database()
.ref('UserList/' + child.key + '/Paymentdetails')
.push(Input)
.then(resp => {
console.log('Done', resp);
})
.catch(err => {
console.log('Error', err);
});
});
});
Firebase view:
You're opening a listener which writes to the same node it listens to. Even if that's not causing recursion, you're still writing a new doc for every child every single time your UserList is updated.
Also, avoid mixing lists and documents in a single rt-db node. That can only lead to pain.
It's difficult to understand what you are trying to do -- but it looks like you might want to call once instead of on, so the listener doesn't stay open and keep writing (potential lots of) new documents.
Additionally, I would recommend not writing to the node you're listening to.
database().ref("SomewhereElse/").push(doc);
I don't know why you would want to push new docs whenever the snapshot updates, you're going to get a lot of duplicates. If that was a mistake you likely want to do those pushes in a onCreate trigger.
i am struggling pretty hard here to find the right solution. Currently, I am using setInterval() to "poll" my server and retrieve an array of objects. To fetch the data, I am using axios. Here are the pertinent functions:
componentDidMount(){
this.timer = setInterval(() => [this.getData(), this.getCustData()], 1000);
}
componentWillUnmount(){
this.timer && clearInterval(this.timer);
this.timer = false
}
getData = () => {
axios.get('http://localhost:3001/api/v1/pickup_deliveries')
.then((response) => {
this.setState({
apiData: response.data
})
})
.catch((error)=>{console.log(error);});
}
getCustData = () => {
axios.get('http://localhost:3001/api/v1/customers')
.then((response) => {
this.setState({
custData: response.data
})
})
.catch((error)=>{console.log(error);});
}
The application is running so slow and often times, it will completely hang the server which makes the whole application unusable. Currently the array it's fetching has over 1000+ objects and that number is growing daily. If I fetch the data without polling the server, the feel of my application is night and day. I am not quite sure what the answer is but I do know what I am doing is NOT the right way.
Is this just the nature of mocking "polling" with setInterval() and it is what it is? Or is there a way to fetch data only when state has changed?
If I need to implement SSE or WebSockets, I will go through the hassle but I wanted to see if there was a way to fix my current code for better performance.
Thanks for hearing me out.
On the frontend side, my advice would be to not use setInterval, but use setTimeout instead.
Using setInterval, your app might send another request even if the response for previous request hasn't come back yet (e. g.: it took more than 1 second). Preferably, you should only send another request 1 second after the previous response is received.
componentDidMount() {
getData();
}
getData = () => {
fetch().then(() => {
updateState();
// schedule next request
setTimeout(getData, 1000);
});
}
You should also try to reduce the amount of updates that need to be done on the frontend, for example by reducing the number of the data.
But nevertheless, I think the most important is to rethink the design of your application. Polling huge JSON that is going to only grow bigger is not a scalable design. It's bad for both the server and the client.
If what you are trying to do is to have the client be notified of any changes in the server side, you should look into WebSocket. A simple idea is that the browser should establish a WS connection to the server. Upon any updates to the server, instead of sending down the whole data, the server should only send down the updates to the client. The client would then update its own state.
For example, let's say 2 users are opening the same page, and one user make changes by adding a new Product. Server will receive this request and update the database accordingly. It will also broadcast a message to all open WebSocket connections (except for the one connection that added the Product), containing a simple object like this:
{
"action": "INSERT",
"payload": {
"product": {
"id": 123123,
... // other product data
}
}
}
The other user will use this data to update its own state so it matches the server.
in all documentation and tutorials for HTTP request i found that recomanded usage is something like:
var ax = {
getCaseData: function() {
api.cases.getCase(caseManager.data.id).then(function(res){
// puting my response to $scope.something
$scope.something = res.data;
});
},
}
I really don't feel like senior so please tell me if I am wrong but I thing many times you don't need to store something like this on scope, for example you want to just resolve promise once and display data (you are not doing anything else with it)
So I was thinking if is there an option to make a promise as:
var ax = {
getCaseData: function() {
return api.cases.getCase(caseManager.data.id);
},
}
and after this call tempalte something like:
<li ng-repeat="ax.getCaseData()"></li>
This was handled automatically in old version of AngularJS (<1.2), but was removed since then.
Some posts state that the feature can be re-enabled manually by adding this line in your ".config" function :
$parseProvider.unwrapPromises(true);
But this is not advised as a solution. You are currently doing it the right way.
If you have plenty of cases like this, you can probably create your own "promises wrapper" function, and use it from your template.
See : https://stackoverflow.com/a/19472065/1636977
I have the following publisher and subscriber code.
It works for the first time when the app starts, but when I try to insert data directly into the Mongo database, it will not automatically update the user screen or I don't see the alert popping.
Am I missing something?
Publish
Meteor.publish('userConnections', function(){
if(!this.userId){
return;
}
return Connections.find({userId: this.userId});
})
Subscribe
$scope.$meteorSubscribe('userConnections').then(function () {
var userContacts = $scope.$meteorCollection(Connections);
alert("subscriber userConnections is called");
if (userContacts && userContacts[0]) {
....
}
}, false);
First off, if you are not using angular-meteor 1.3 you should be. The API has changed a lot. $meteorSubscribe has been deprecated!
To directly answer your question, $meteorSubscribe is a promise that gets resolved (only once) when the subscription is ready. So, it will only ever be called once. If you look at the documentation for subscribe you'll see how to make the binding "reactive", by assigning it to a scope variable. In your case it would be something like:
$scope.userContacts = $scope.$meteorCollection(Connections);
Doing it this way, when the collection gets updated, the $scope.userContacts should get updated as well.