0

Hi I'm trying to make an automated update on a list of articles whenever the user changes his preferred language.

The way I'm trying to do this is by having a IO socket update whenever the user changes in the database.

However I seem to be unsuccesfull in my endeavors, and I have no idea as to why.

Since I'm new to socket.io I thought I'll ask the coding gods in here for some help.

May the software be with you ^^

PS: the project is a Angular fullstack project, scaffolded with Yeoman


Code time!


client/ components/ articlebar/ articlebar.controller.js

'use strict';

angular.module('unityAcademyApp')
.controller('ArticlebarCtrl', function ($scope, $location, Auth, socket) {
  $scope.articles = {};

  function populateArticles(){ 
       ...
        Some functionality where $scope.articles are set
        ...
    };

    socket.syncUpdates('user', $scope.articles, function() {
        console.log('hit');
        populateArticles();
    });
});


client/ components/ socket/ socket.service.js

/* global io */
'use strict';

angular.module('unityAcademyApp')
  .factory('socket', function(socketFactory) {

    // socket.io now auto-configures its connection when we ommit a connection url
    var ioSocket = io('', {
      // Send auth token on connection, you will need to DI the Auth service above
      // 'query': 'token=' + Auth.getToken()
      path: '/socket.io-client'
    });

    var socket = socketFactory({
      ioSocket: ioSocket
    });

    return {
      socket: socket,

      /**
       * Register listeners to sync an array with updates on a model
       *
       * Takes the array we want to sync, the model name that socket updates are sent from,
       * and an optional callback function after new items are updated.
       *
       * @param {String} modelName
       * @param {Array} array
       * @param {Function} cb
       */
      syncUpdates: function (modelName, array, cb) {
        cb = cb || angular.noop;

        /**
         * Syncs item creation/updates on 'model:save'
         */
        socket.on(modelName + ':save', function (item) {
          var oldItem = _.find(array, {_id: item._id});
          var index = array.indexOf(oldItem);   // this is line 39
          var event = 'created';

          // replace oldItem if it exists
          // otherwise just add item to the collection
          if (oldItem) {
            array.splice(index, 1, item);
            event = 'updated';
          } else {
            array.push(item);
          }

          cb(event, item, array);
        });

        /**
         * Syncs removed items on 'model:remove'
         */
        socket.on(modelName + ':remove', function (item) {
          var event = 'deleted';
          _.remove(array, {_id: item._id});
          cb(event, item, array);
        });
      },

      /**
       * Removes listeners for a models updates on the socket
       *
       * @param modelName
       */
      unsyncUpdates: function (modelName) {
        socket.removeAllListeners(modelName + ':save');
        socket.removeAllListeners(modelName + ':remove');
      }
    };
  });


server/ config/ socketio.js

/**
 * Socket.io configuration
 */

'use strict';

var config = require('./environment');

// When the user disconnects.. perform this
function onDisconnect(socket) {}

// When the user connects.. perform this
function onConnect(socket) {
    // When the client emits 'info', this listens and executes
    socket.on('info', function (data) {
        console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
    });

    // Insert sockets below
    require('../api/translation/translation.socket').register(socket);
    require('../api/comment/comment.socket').register(socket);
    require('../api/article/article.socket').register(socket);
    require('../api/language/language.socket').register(socket);
    require('../api/thing/thing.socket').register(socket);
    require('../api/user/user.socket').register(socket);
}

module.exports = function (socketio) {
    // socket.io (v1.x.x) is powered by debug.
    // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
    //
    // ex: DEBUG: "http*,socket.io:socket"

    // We can authenticate socket.io users and access their token through socket.handshake.decoded_token
    //
    // 1. You will need to send the token in `client/components/socket/socket.service.js`
    //
    // 2. Require authentication here:
    // socketio.use(require('socketio-jwt').authorize({
    //   secret: config.secrets.session,
    //   handshake: true
    // }));

    socketio.on('connection', function (socket) {
        socket.address = socket.handshake.address !== null ?
            socket.handshake.address.address + ':' + socket.handshake.address.port :
            process.env.DOMAIN;

        socket.connectedAt = new Date();

        // Call onDisconnect.
        socket.on('disconnect', function () {
            onDisconnect(socket);
            console.info('[%s] DISCONNECTED', socket.address);
        });

        // Call onConnect.
        onConnect(socket);
        console.info('[%s] CONNECTED', socket.address);
    });
};


server/ api/ user/ user.socket.js

/** * Broadcast updates to client when the model changes */

'use strict';

var User = require('./user.model');

exports.register = function(socket) {
  User.schema.post('save', function (doc) {
    onSave(socket, doc);
  });
  User.schema.post('remove', function (doc) {
    onRemove(socket, doc);
  });
}

function onSave(socket, doc, cb) {
  socket.emit('user:save', doc);
}

function onRemove(socket, doc, cb) {
  socket.emit('user:remove', doc);
}

Errors encountered so far

So far I get the following error when running the code

TypeError: array.indexOf is not a function
    at Socket.<anonymous> (socket.service.js:39)
    at socket.js:24
    at angular.js:17782
    at completeOutstandingRequest (angular.js:5490)
    at angular.js:5762
        (anonymous function)        @ angular.js:12416
        $get                        @ angular.js:9203
        (anonymous function)        @ angular.js:17785
        completeOutstandingRequest  @ angular.js:5490
        (anonymous function)        @ angular.js:5762

3 Answers 3

1

I'm not sure why you get that error, but I think I know why your data isn't updating.

You have to wrap your callback functions inside a $timeout function in order to trigger your changes. For example, you could do this:

$timeout(function(){
    cb(event, item, array);
}, 0);

Remember to include $timeout directive in your socket factory.

Sign up to request clarification or add additional context in comments.

3 Comments

I have been using this scaffolding tool before, where I didn't have a $timeout function for triggering the changes, socket.io normally takes care of that by itself. The triggering works when I update items such as article but not when I update user. Can you elaborate on why you think it whould be necessary for this case?
Oh, forget about the scaffolding part. It happened to me once because I was using raw socket.io library, so it didn't trigger the $scope changes. I had finished using something like this: gist.github.com/muZk/829c7e845f3098bc5c76 About the error, check your "array" set function, it may be an object instead of an array at some point of the execution.
ah I see, in that case I understand why you whould use it ^^ About the array, the object olditem is undefined for some reason, and I'm running through the code to see why. Without luck however
0

what's 'undescore' mean? I'm not sure about the 'undescore', but I guess that's alias for 'this'. I think you should init var _ = this.

I just guessing.

Comments

0

I found the problem.

The error where due to it looking for a user in a list of articles, which returned undefined due to there not being any match. The solution was therefore to change the code in client/ components/ articlebar/ articlebar.controller.js

from

socket.syncUpdates('user', $scope.articles, function() {
    console.log('hit');
    populateArticles();
});

to

socket.syncUpdates('user', $scope.users, function() {
    console.log('hit');
    populateArticles();
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.