0

Complete Revision Of Question

Since this question has evolved into something completely different, I figured it would be best if I explained the current situation again from scratch.

I'm trying to create a simple messageboard with Node, Express, MongoDB and Angular.

I have a get and post function, which seem to be working fine on the server side, I get no errors and my data is being added/retrieved to/from the database.

The problem lies with my client-side getMessages() function.

In a normal situation I would like to call getMessages() on the initialization of the website, and after posting a message. But for simplicity I created a button that calls "getMessages()" when pressed.

The first time I press this button, a GET-request is sent out and all my messages are retrieved and displayed in the view.

But when I post a message, and hit the 'get-messages-button' again, no new GET request is being sent out, and the function returns the old list (the one retrieved with the first, and only, GET-request)

I've been stuck on this for 3 days now and I really can't understand what I'm doing wrong. The getMessages() function is being called, but it seems as if it stored the 'response.data' from the first GET request somewhere and immediately returns this same data, without getting the chance to send a new GET request.

Code:

Controller

(function() {
'use strict';

angular
.module('app')
.controller('MainController', mainController);

function mainController(navigationFactory, $http, $timeout, apiFactory, $scope) {

    var that = this; // jshint ignore: line

    function init() {
        //that.getMessages();
    }

    that.getMessages = function() {
        return apiFactory.getMessages().then(function(messages) {
            that.messages = messages;

        });
    }

    that.postMessage = function() {

        apiFactory.postMessage(that.message).then(function(response) {
            console.log('Posted from controller', response);               
        }).catch(function(e) {
            console.log(e);
        });

    }

    init();   
}
})();

Service

(function() {
'use strict';

angular
    .module('app')
    .factory('apiFactory', apiFactory);

function apiFactory($interval, $localStorage, $sessionStorage, $http) {

    var factory = {};

    factory.postMessage = function(message) {
        return $http.post('http://localhost:5000/api/message', {msg: message}).then(function(response) {
                return response;
        });       
    }

    factory.getMessages = function() {
        var promise = $http.get('http://localhost:5000/api/message').then(function(response) {
                console.log('data:', result.data); //Immediately returns old list without sending new GET request
                return response.data;
        });    

        return promise;        
    }

    return factory;
}

})();

Server.js (Just in case it turns out to be my server-side code anyway)

var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');


var Message = mongoose.model('Message', {
    msg: String
});
var app = express();


app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
    next();
})

app.get('/api/message', GetMessages);

app.post('/api/message', function(req, res) {
    console.log(req.body);

    var message = new Message(req.body);
    message.save(function(err) {
        if(err) {
            console.log(err);
        } else {
            res.status(200).send('Message added');
        }
    }); 
})

function GetMessages(req, res) {
    console.log('Getting messages from server');
    Message.find({}).exec(function(err, result) {
        if(err) {
            return res.status(500).send(err);
        }
        return res.status(200).json(result);
    });

    console.log('Got messages from server');
}

mongoose.connect("mongodb://localhost:27017/mydb", function(err, db) {
    if(!err) {
        console.log("Connected to mongoDB!");   
    }
})

var server = app.listen(5000, function() {
    console.log('listening on port ', server.address().port);
})

UPDATE

This might probably help in illustrating what exactly the problem is.

I have a function that is called when I press a button on my view:

    that.getMessages = function() {
         $http.get('http://localhost:5000/api/message'); // <-- Only happens once
         console.log('get request sent'); // <-- Gets logged everytime 
    };

All I'm doing is sending a get request to my server. Nothing with promises yet. The first time I press my button, I can see a GET request in my debugger networks tab, which has a status of 304 and a response of my array of messages.

Now when I press the button for the 2nd time, no GET request is being sent out. I can log messages so I know my function is being called, but It just seems to ignore my $http.get()...

Max Taylor
  • 343
  • 3
  • 16
  • I don't see where you're trying to get the messages again after a post. – JB Nizet Nov 07 '16 at 21:15
  • Sorry, I'll add it to the code for clarity. I removed it earlier because it didn't work. – Max Taylor Nov 07 '16 at 21:19
  • You have done 7 updates to the question. What is the current status of the question? Also please update the title of the question. The problem seems to have nothing to do with applying data to scope. The problem seems to be with sequencing a GET operation after a POST. – georgeawg Nov 08 '16 at 15:30
  • Yes, this became clear after discussing with the current Answer, if you have time to read through it, you'll probably understand. But I'll change the post and title to make it more clear. – Max Taylor Nov 08 '16 at 15:34

1 Answers1

3

Your code gets the messages immediately after you posted the new message. So you have no guarantee that the second request will be processed by the server after the first one. There is a big chance they will be processed concurrently. You need to get the messages after the post has been processed, i.e. when the asynchronous response to the first request has come back.

So, first, you need to be able to know when the post has succeeded. So you need to return the http promise, to know if it's resolved or rejected:

factory.postMessage = function(message) {
    return $http.post('http://localhost:5000/api/message', {msg: message});
}

Then you need to get the messages after a successful post:

that.postMessage = function() {
    apiFactory.postMessage(this.message).then(function() {
        that.getMessages();
    });
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks! Sounds very logical. Unfortunately though, my view still isn't being updated after posting a message... I still need to refresh. – Max Taylor Nov 07 '16 at 21:30
  • I tried logging to the console inside the .then() function, but it looks like it never calls .then() – Max Taylor Nov 07 '16 at 21:54
  • That means that the post doesn't succeed. Look at your browser dev tools, in the network tab, what you get as a response. – JB Nizet Nov 07 '16 at 21:56
  • But the post does succeed, as the message is inserted into the database. In my network tab I send a post with status 200, but the response tab is empty EDIT: It does not have status 200, but status pending, and eventually fails. – Max Taylor Nov 07 '16 at 22:01
  • 1
    Ah, after waiting long enough I suddenly get this error: POST http://localhost:5000/api/message net::ERR_EMPTY_RESPONSE Maybe I'm not sending a correct callback with the post? – Max Taylor Nov 07 '16 at 22:04
  • Something is wrong with the network or the server, probably. – JB Nizet Nov 07 '16 at 22:07
  • See my edited post. For some reason my .then() statement caused the response status stay on pending, but when I added .send() to the response, my .then() statement worked. It's still not working tho.. :/ – Max Taylor Nov 07 '16 at 22:15
  • You'll need to elaborate. What doesn't work? Is the second GET request coing out? Do you get a successful response? Does the response contain what you expect it to contain? My guess is that it's a server problem: it probably sends back the same list as before. – JB Nizet Nov 07 '16 at 22:33
  • The GET request is not going out. But the that.getMessages() is being called inside the .then() function. – Max Taylor Nov 07 '16 at 22:37
  • Use your debugger, and step through the code, until $http is called. – JB Nizet Nov 07 '16 at 22:40
  • It seems you were right. in my apiFactory the getMesssages() function never fires the get request. It returns the old promise. How would I get it to wait for the get request before returning? – Max Taylor Nov 07 '16 at 22:50
  • The code you posted does send a new http request and returns a new promise. Use that code. – JB Nizet Nov 07 '16 at 22:53
  • I'm not sure what code you mean? You mean my initial that.postMessage() function? I tried calling that.getMessages() outside of the .then() function, but still the factory.getMessages() keeps returning the old list before a GET request is being sent out. – Max Taylor Nov 08 '16 at 07:26
  • I don't see how that would be possible. This method sends a GET request every time it's called. – JB Nizet Nov 08 '16 at 07:30
  • What it looks like to me is that the method does get called, but before the HTTP GET can return data, the variable promise is already returned. (with the old data) but I dont understand why. – Max Taylor Nov 08 '16 at 07:37
  • The promise, which is a new promise, is of course returned immediately. That's the principle of a promise. But it's returned in a pending state. It will be resolved later, when the HTTP response comes back. – JB Nizet Nov 08 '16 at 07:40
  • I put a console.log() before and after the GET request. They are both shown in my console, but no GET request is going out... – Max Taylor Nov 08 '16 at 08:22
  • For some reason my factory.getMessages() does run in the .then() function but it never sends the HTTP Get. Isn't .then() supposed to wait for the HTTP GET to get a response? – Max Taylor Nov 08 '16 at 09:12
  • No. If it waited, you would have synchronous code blocking the unique JavaScript thread, and the UI would freeze until the response comes back.then() simply tells the promise what to do, later, when the response comes back and is successful. It also cretes another promise. Read http://blog.ninja-squad.com/2015/05/28/angularjs-promises/. How do you determine that no HTTP request is sent? – JB Nizet Nov 08 '16 at 12:21
  • Thanks for the article. I'll try changing my code to their style. I determine this by checking the network tab in chrome. – Max Taylor Nov 08 '16 at 12:34
  • Still not working, even with their code. I'm also getting no error messages. I tried adding an error callback like they do in the article, but this only works with .success().error(), and not with a .then() statement. And when I use success() instead of then(), I get undefined. – Max Taylor Nov 08 '16 at 12:41
  • Ok, so now I'm using the $q service as they are in that article, and I got it to work again, but still I can see every part of the code is being executed, and I do reach the .then() statement where I set messages to the scope. But if I console.log messages, then I see the old list, and I see in my networks tab there was no new GET request – Max Taylor Nov 08 '16 at 13:03
  • I am the author of that article. You shouldn't use $q. The code of getMessages that you have in your question is fine. Edit your question and post the code you are now using. – JB Nizet Nov 08 '16 at 13:19
  • I edited my post with current code. If you want I can change it back to how it was before using $q, but since this is working as it was before I thought I would leave it like it is. – Max Taylor Nov 08 '16 at 13:31
  • Except for the defer anti-pattern that should be replaced by chaining, as my article explains, I see nothing wrong. What is printed in the console? – JB Nizet Nov 08 '16 at 13:34
  • I added console.log's to my code, everything gets logged. I get no other errors in my console, and nothing in my network tab – Max Taylor Nov 08 '16 at 13:47
  • I have the feeling that your network tab is configured to hide XHR requests somehow. Do you see the initial GET request used to load the initial messages? Are you sure the server does return the new list. This is probably where the problem is. – JB Nizet Nov 08 '16 at 13:49
  • Yes, the initial GET request is in my network tab, it has a response status of 304 (not modified) and a response with my list of messages. After I post a message and refresh the page, this GET request does get the new list with my new message – Max Taylor Nov 08 '16 at 13:52
  • The only thing I can imagine is that you have an $http interceptor somewhere which tells $http to cache your request. – JB Nizet Nov 08 '16 at 14:24
  • I tried adding the code from this question: http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers to stop it from caching, and I have worked with different code to stop this from happening before, but with both scenarios nothing changes. Both my POST and GET requests have this line: 'Cache-Control:no-cache, no-store, must-revalidate' – Max Taylor Nov 08 '16 at 14:39
  • You were right... The angular skeleton I used had a $http interceptor in the config.... woopsie. Thanks for you help anyway! – Max Taylor Nov 09 '16 at 21:26