0

I am learning Backbone.js and as a trial project I am creating a little WordPress user management application. So far my code shows a listing of all WordPress users and it has a form which enables you to add new users to the application.

This all works fine however when you add a new user the listing of users doesn't update automatically, you need to refresh the page to see the new user added which isn't ideal and defeats one of the benefits of Backbone.js!

I have a model for a user and then a collection which compiles all the users. I have a view which outputs the users into a ul and I have a view which renders the form. How do I make my code work so when the .save method is called the view which contains the users updates with the new user? Or is there another way to approach this?

//define the model which sets the defaults for each user
var UserModel = Backbone.Model.extend({
    defaults: {
        "username": "",
        "first_name": "",
        "last_name": "",
        "email": "",
        "password": "",
    },
    initialize: function(){
    },
    urlRoot: 'http://localhost/development/wp-json/wp/v2/users'
});

//define the base URL for ajax calls
var baseURL = 'http://localhost/development/wp-json/wp/v2/';

//function to define username and password
function authenticationDetails(){
    var user = "myUserName";
    var pass = "myPassword";
    var token = btoa(user+':'+pass);
    return 'Basic ' + token;
}

//add basic authorisation header to all API requests
Backbone.$.ajaxSetup({
    headers: {'Authorization':authenticationDetails()}
});

//create a collection which returns the data
var UsersCollection = Backbone.Collection.extend(
    {
        model: UserModel,
        // Url to request when fetch() is called
        url: baseURL + 'users?context=edit',
        parse: function(response) {
            return response;
        },
        initialize: function(){
        }
    });

// Define the View
UserView = Backbone.View.extend({
    model: UserModel,
    initialize: function() {
      // create a collection
      this.collection = new UsersCollection;
      // Fetch the collection and call render() method
      var that = this;
      this.collection.fetch({
        success: function () {
            that.render();
        }
      });
    },
    // Use an external template
    template: _.template($('#UserTemplate').html()),
    render: function() {
        // Fill the html with the template and the collection
        $(this.el).html(this.template({ users: this.collection.toJSON() }));
        return this;
    },

});

var userListing = new UserView({
    // define the el where the view will render
    el: $('#user-listing')
});

NewUserFormView = Backbone.View.extend({
    initialize: function() {
      this.render();
    },
    // Use an external template
    template: _.template($('#NewUserTemplate').html()),
    render: function() {
        // Fill the html with the template and the collection
        $(this.el).html(this.template());
        return this;
    },
    events: {
        'click .create-user':'addNewUser'
    },
    addNewUser: function(){

        var newFirstName = $('.first-name').val();
        var newLastName = $('.last-name').val();
        var newEmail = $('.email').val();
        var newPassword = $('.password').val();
        var newUserName = newFirstName.toLowerCase();

        var myNewUser = new UserModel({username:newUserName,first_name:newFirstName,last_name:newLastName,email:newEmail,password:newPassword});
        console.log(myNewUser);
        myNewUser.save({}, {
            success: function (model, respose, options) {
                console.log("The model has been saved to the server");
            },
            error: function (model, xhr, options) {
                console.log("Something went wrong while saving the model");
            }
        });
    }
});

var userForm = new NewUserFormView({
    // define the el where the view will render
    el: $('#new-user-form')
});
user1190132
  • 515
  • 4
  • 18
  • 1
    Don't use `ajaxSetup` to manage authenticated calls. Instead, [use Backbone's `sync` function to your advantage](https://stackoverflow.com/a/41991573/1218980). – Emile Bergeron Jul 18 '17 at 13:43
  • 1
    [`$(this.el) === this.$el`](https://stackoverflow.com/a/40321584/1218980). – Emile Bergeron Jul 18 '17 at 13:45
  • 1
    `model: UserModel,` in a view is useless. The view doesn't need a Model constructor. – Emile Bergeron Jul 18 '17 at 13:47
  • 1
    And [be careful with base64 encoding with `btoa`](https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding). – Emile Bergeron Jul 18 '17 at 13:50
  • Thanks Emile, I am going to implement all the comments you made into the application. It's really helpful and good to know. I am just learning backbone so it's great to get these pointers! – user1190132 Jul 19 '17 at 04:17

1 Answers1

2

All backbone objects (models, collections, views) throw events, some of which would be relevant to what you want. Models throw change events when their .set methods are used, and Collections throw add or update events... a complete list is here.

Once you know which events are already being thrown, you can listen to them and react. For example, use listenTo - in your view's initialize, you can add:

this.listenTo(this.collection, 'add', this.render);

That will cause your view to rerender whenever a model is added to your collection. You can also cause models, collections, whatever, to throw custom events using trigger from anywhere in the code.

EDIT: For the specific case of getting your user listing view to rerender when a new user is added using the form, here are the steps you can take... In the initialize method of your UserView, after the initialize the collection, add:

this.listenTo(this.collection, 'add', this.render);

Then in your form view... assuming you want to wait until the save is complete on your server, in the addNewUser method, in the success callback of your save, add:

userlisting.collection.add(model);

This will work, since the instance of your UserView is in the global scope. Hope this one works for you!

arbuthnott
  • 3,819
  • 2
  • 8
  • 21
  • Hi arbuthnott, I added this code, and I also tried this before but it still doesn't work. Is the issue that I'm not adding the model to the collection? Does .save only do the API call not actually add the model to the collection? I would really appreciate it if you could be more specific around my code on what would work as this will help me understand. Thanks – user1190132 Jul 18 '17 at 02:49
  • 1
    @user1190132 Sure, I had a look through your code, and I can edit my answer to be more specific. Basically, you are right, it is because you are not adding your user to the collection, so no 'add' event would fire. – arbuthnott Jul 18 '17 at 09:34
  • Thanks @arbuthnott, unfortunately this still isn't working for me. Maybe I am not following your instructions well. I have tried out all sorts of different combinations and it doesn't seem to work. I get the principal of what needs to happen but I can't enact it properly. I think I need to learn more about the fundamentals... – user1190132 Jul 18 '17 at 10:05
  • 1
    Hmm, not sure what's wrong then. Here are a couple of random ideas... I notice that when you create your collection, you leave out the invocation brackets: `new UsersCollection` as opposed to `new UsersCollection()`. I don't think think that's causing you problems, but who knows... second idea, if your collection is supposed to be all users, then in your save callback, you could try re-fetching - `userListing.collection.fetch()`. If none of this is working I'd try logging the collection in the success callback `console.log(userListing.collection)` to make sure you have the right object. – arbuthnott Jul 18 '17 at 11:54
  • Ahhr awesome, the userListing.collection.fetch() in the success callback did the trick!!! I really appreciate your help it means a lot :) – user1190132 Jul 19 '17 at 04:16
  • Glad it worked out! I've been using backbone at work for a year or so, and I've come to really appreciate it. It's a bit more work than a lot of the other "hot" client side libraries, but I feel like it's much more adaptable. It just has a pretty useful bag of tools already defined that you can use however you like. I tend to use it's Fetch-and-save functions less, but make heavy use of its events hash and modular properties. – arbuthnott Jul 19 '17 at 09:46
  • Yeah I am excited to get into it more, now I can carry out simple crud operations it will enable me to try out other things. I have been using jQuery to do my own backbone like functions so far but I like the structure that backbone provides – user1190132 Jul 19 '17 at 16:12