1

I'm using the module pattern to build a small quiz in a modal, which is nearly complete. There is a button at the end of the quiz that says "try again" that will restart the quiz, and I'm having a hard time thinking of a DRY way to reset the settings property (a list of vars for the app) to its default value.

This what I have right now:

jQuery(function($) {
    var s,
        QuizApp = {
            settings: {
                timer: null,
                isPaused: false,
                count: null,
                correctAnswer: null,
                score: 0,
                questionCounter: 1
            },

            init: function() {
                s = this.settings;
                this.questionsGet();
                this.bindUIActions();
            },

            // Code omitted for event handlers and such

            resetQuiz: function() {
                s.timer = null;
                s.isPaused = false;
                s.count = null;
                s.correctAnswer = null;
                s.score = 0;
                s.questionCounter = 1;
            },
        },
}; $(function() {
    QuizApp.init();
});

});

Not very DRY - pretty bad actually. What I want is:

jQuery(function ($) {
    var s,
        QuizApp = {
            settings: {
                timer: null,
                isPaused: false,
                count: null,
                correctAnswer: null,
                score: 0,
                questionCounter: 1,
                defaults: { /* pseudo code to store settings here */ }
            },
            resetQuiz: function () {
                s = s.defaults // pseudo code to reset the settings to the value stored in defaults
            },
        };
    $(function () {
        QuizApp.init();
    });
});

Or something better, if you can think of it. I've done tons of searching for "reset object properties" and "clone object properties," but I've found nothing that explains how to do this. jQuery is ok to use, obviously.

Furthermore, I don't understand how/where to store the data and how to map the data back in because I don't fully understand what type of data this is (is "settings" an object property, and it's values sub-properties? Or is it a multi-dimensional array? What do I store the data in to retrieve its default values later? etc), so any kind of in-depth explanation or links to info of the data types we're working with here is an added bonus!

3
  • 1
    Why not keep the defaults as a private variable and then assign a clone of the object to settings whenever needed? Commented Nov 21, 2013 at 21:17
  • What in the world? Someone edited my code and massively butchered it. Fixed now, please look again. Commented Nov 21, 2013 at 21:28
  • @Shreyas - that sounds like a great way to go. Would you mind giving me a code example please? Commented Nov 21, 2013 at 21:29

3 Answers 3

3

Create a variable outside of your QuizApp called defaults and assign your default values to it.

Then when you declare QuizApp settings: $.extend({},defaults)

Then inside your restartQuiz() function declare s = $.extend({},defaults);

I would also recommend wrapping your code with

; (function($){

    "strict mode";

})();

This will prevent conflict with overwriting outside variables

; (function(){
"strict mode";

jQuery(function($) {
var s,
    defaults = {
        timer: null,
        isPaused: false,
        count: null,
        correctAnswer: null,
        score: 0,
        questionCounter: 1
    },
    QuizApp = {
        settings: $.extend({},defaults),

        init: function() {
            s = $.extend({},defaults);
            this.questionsGet();
            this.bindUIActions();
        },

        resetQuiz: function() {
           s = $.extend({},defaults); // pseudo code to reset the settings to the value stored in defaults
        },

    };
$(function() {
    QuizApp.init();
});

})(jQuery);
Sign up to request clarification or add additional context in comments.

5 Comments

Shawn, I think this is what I'm looking for. I realized I left out the init code, so the "s" var wasn't clear what its purpose was. I updated my question so it's now clearer - would you mind updating your answer to match (if necessary?) I'm testing right now, will accept if it works
@IvanDurst I updated my code to match your question code and I made a suggestion to protect your code from possible conflict with any outside code
Shawn, this is fantastic, and I got it working, thank you. A couple edits I made to your code: the comma after closing bracket for "defaults" needs to be a semicolon; added "var" before the QuizApp object (I don't know if this is necessary)
@IvanDurst I had defaults as a separate variable apart from QuizApp, that is why there was no semi-colon, but what you did, as you said, works just fine :)
ohhh, I get it. So you didn't need to add "var" again, because they were a comma separated list of vars. Makes perfect sense, thank you for clarifying.
2

Don't set settings to be the defaults. Rather copy them.

// set up the defaults somewhere in your module
var defaults = {/* stuff */};

// then when initing and resetting, use `$.extend` or your clone flavor
// of choice to assign `s` a new copy of the defaults
s = $.extend({}, defaults);

This way s.foo = "bar" doesn't actually change the defaults object.

1 Comment

+1: Mentioning conflict with assigning s = defaults; directly
1

I think you're looking for something like this if you really don't want to keep a second copy of the object with the default values:

function PropertyHolder (defaultValue) {
    this.defaultValue = defaultValue;
    this.currentValue = defaultValue;
}

PropertyHolder.prototype.reset = function () {
    this.currentValue = this.defaultValue;
};

PropertyHolder.prototype.setValue = function (newValue) {
    this.currentValue = newValue;
};

PropertyHolder.prototype.getValue = function () {
    return this.currentValue;
};

function Settings (settings) {
    for( var key in settings ) {
        this[key] = new PropertyHolder( settings[key] );
    }
};

Settings.prototype.reset = function () {
    for( var key in this ) {
        if( this[key] instanceof PropertyHolder ) {
            this[key].reset();
        }
    }
};

var settings = new Settings( {
    valueA: 42,
    valueB: ["Hello", "World"]
} );

console.log( settings.valueA.getValue() ); // 42
settings.valueA.setValue( -1 );
console.log( settings.valueA.getValue() ); // -1
settings.reset();
console.log( settings.valueA.getValue() ); // 42

As most things, this can be massively improved. It's just to get the idea across. Though, for a small simple settings object, I don't think there's anything wrong with just keeping a second copy with default values. It might be a little bit of duplication, but in that case it's not all that bad.

However, this might also work for you to avoid duplication:

var defaults = {
    'valueA': 42,
    'valueB': ["Hello", "World"]
};

var settings = {};

var getSetting = function (setting) {
    return typeof settings[setting] !== 'undefined' ? settings[setting] : defaults[setting];
};
var resetSettings = function () {
    settings = {};
};

console.log( getSetting( 'valueA' ) ); // 42
settings['valueA'] = -1;
console.log( getSetting( 'valueA' ) ); // -1
resetSettings();
console.log( getSetting( 'valueA' ) ); // 42

I am not too much of a fan of using string identifiers, but most people don't seem to mind.

3 Comments

I appreciate you taking the time to write this out. A little bit of overkill for my situation... if I can't get one of the other answers working, I'll take your suggestion about the default values.
No problem. Check out the second solution I added.
I should add that mine is vanilla JS. Since you're using jQuery (obviously), the extend method might be the best. It's somewhat similar to my second solution, though not quite the same (I don't copy the object).

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.