Search code examples
node.jsexpressbackbone.js

Backbone and Express: concatinating (duplicating) routes on res.redirect


I have an action where I need to update MongoDB entry including _id field, which requires deleting old entry and making a new one, here is server side:

exports.update = function(req, res, next){
  var outcome = [];

  outcome.previousId = req.params.id;
  outcome.newId = req.body.name;

  var getPreviousRecord = function(callback) {
    req.app.db.models.AccountGroup
      .findOne({ _id: req.params.id })
      .lean()
      .exec(function(err, accountGroups) {
        if (err) {
          return callback(err, null);
        }

        outcome.accountGroups = accountGroups;
        return callback(null, 'done');
      });
  };

  var makeNewRecord = function(callback) {
    var permissions = outcome.accountGroups.permissions;
    var fieldsToSet = {
      _id: outcome.newId.toLowerCase(),
      name: outcome.newId,
      permissions: permissions
    };

    req.app.db.models.AccountGroup
      .create(fieldsToSet, function(err, record) {
        if (err) {
          return callback(err, null);
        }

        outcome.record = record;
        return callback(null, 'done');
      });
  };

  var deletePreviousRecord = function() {
    req.app.db.models.AccountGroup
      .findByIdAndRemove(outcome.previousId)
      .exec(function(err) {
        if (err) {
          return next(err);
        }

        res.redirect('admin/account-groups/' + outcome.newId + '/');
      });
  };

  var asyncFinally = function(err) {
    if (err) {
      return next(err);
    }
  };

  require('async').series([getPreviousRecord, makeNewRecord, deletePreviousRecord], asyncFinally);
};

It works fine, but I can't make this work normally on the front-end, it returns me both old route and a new route, for example:

PUT /admin/account-groups/customers22/admin/account-groups/Customers2233/ 404 213.749 ms - 31

where customers22 is old _id and customers2233 is new _id. If I navigate from another page to new entry it gets route normally.

On client side:

(function() {
  'use strict';

  app = app || {};

  app.Details = Backbone.Model.extend({
    idAttribute: '_id',
    defaults: {
      success: false,
      errors: [],
      errfor: {},
      name: ''
    },
    url: function() {
      return '/admin/account-groups/'+ app.mainView.model.id +'/';
    },
    parse: function(response) {
      if (response.accountGroup) {
        app.mainView.model.set(response.accountGroup);
        delete response.accountGroup;
      }

      return response;
    }
  });

  app.DetailsView = Backbone.View.extend({
    el: '#details',
    events: {
      'click .btn-update': 'update'
    },
    template: Handlebars.compile( $('#tmpl-details').html() ),
    initialize: function() {
      this.model = new app.Details();
      this.syncUp();
      this.listenTo(app.mainView.model, 'change', this.syncUp);
      this.listenTo(this.model, 'sync', this.render);
      this.render();
    },
    syncUp: function() {
      this.model.set({
        _id: app.mainView.model.id,
        name: app.mainView.model.get('name')
      });
    },
    render: function() {
      this.$el.html(this.template( this.model.attributes ));

      for (var key in this.model.attributes) {
        if (this.model.attributes.hasOwnProperty(key)) {
          this.$el.find('[name="'+ key +'"]').val(this.model.attributes[key]);
        }
      }
    },
    update: function() {
      this.model.save({
        name: this.$el.find('[name="name"]').val()
      });
    }

  });

  app.MainView = Backbone.View.extend({
    el: '.page .container',
    initialize: function() {
      app.mainView = this;
      this.model = new app.AccountGroup( JSON.parse( unescape($('#data-record').html()) ) );

      // ...
      app.detailsView = new app.DetailsView();
    }
  });

  $(document).ready(function() {
    app.mainView = new app.MainView();
  });   
}());

It probably requires to trigger both model.save and model.destroy or prevent URL being used. Any advice on how to do it is appreciated, thank you.

Edit Just a typo mistake here that is not related to the question, recklessly checking routes, see as cancelled


Solution

  • I believe the problem is here:

    res.redirect('admin/account-groups/' + outcome.newId + '/');
    

    That's a relative path so it'll be appended onto the current URL. I suspect you want something like this:

    res.redirect('/admin/account-groups/' + outcome.newId + '/');