Accessing one javascript class inside another - javascript

I have two javascript “classes”. One of them is supposed to be a sort of general one, that will instantiate another sub-classes.
Somehow this throws an undefined is not a function error:
this.progress = new Uploader.Progress({
matcher: options.matcher,
});
I'm using underscore as a dependency included through the Rails asset pipeline require statement. Here is the full code:
//= require underscore
if (typeof Uploader === "undefined") {
var Uploader = {};
}
(function() {
var Progress = Uploader.Progress = function(options) {
options || (options = {});
if(options.matcher) this.$matcher = $(options.matcher);
this.initialize.apply(this, arguments);
};
_.extend(Progress.prototype, {}, {
initialize: function() {
this.listen();
},
listen: function() {
this.$matcher.on("fileuploadprogress", function(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
data.context.find(".upload-progress").css({ "width": progress + "%" });
});
return this;
},
});
})();
(function() {
var Uploader = Project.Uploader = function(options) {
options || (options = {});
if(options.url) this.url = options.url;
if(options.matcher) this.$matcher = $(options.matcher);
this.progress = new Uploader.Progress({
matcher: options.matcher,
});
this.initialize.apply(this, arguments);
};
_.extend(Uploader.prototype, {}, {
initialize: function() {
this.listen();
},
listen: function() {
var _this = this;
this.$matcher.fileupload({
url: this.url,
type: "POST",
dataType: "json",
add: function(e, data) {
data.context = _this.$matcher.closest("form");
data.submit()
.success(function(result, textStatus, jqXHR) {
console.log("submitted");
});
},
});
return this;
},
});
})();
var uploader = new Project.Uploader({
matcher: "#video_file",
url: "/users/1/videos",
});

When you say
this.progress = new Uploader.Progress({
matcher: options.matcher,
});
it matched the Uploader defined in thefunction scope` which is
var Uploader = Project.Uploader = function(options) {
and this one doesn't have a property Progress so Uploader.Progress is undefined. Hence, the error.
To fix that, change
var Uploader = Project.Uploader = function(options) {
To
var SomeOtherVariable = Project.Uploader = function(options) {
so now when you call new Uploader.Progress({ it will start looking for Uploader outside the function scope as it will not find it within the function scope. The correct function set for Uploader.Progress in the global scope would be called.

u create an uploader object on module scope
if (typeof Uploader === "undefined") {
var Uploader = {};
}
but then u create another local one
var Uploader = Project.Uploader = function(options) ...
binding anything on *this in the local object is not visible in the global one. that is a very strange style.

Related

"this" returning a function instead of object

In the below javascript code I wanted to know which object does "this" point to.But instead I am getting a function.
I thought "this" always referred to the object calling that function.
So can anyone explain why this behaviour ?
$(document).ready(function() {
var Paper = function() {};
Paper.prototype = {
populate: function(data) {
data.questions.forEach(function(entry) {
//printing a function instead of object
console.log(this);
}.bind(this));
}
};
var paperDataLoader = function() {
this.callbacks = [];
};
paperDataLoader.prototype = {
//accepts a callback to notify the registered function
registerDataLoaded: function(callback) {
this.callbacks.push(callback);
},
//calls the callback functions when data is loaded
loadData: function() {
$.ajax('../find_paper.php', {
method: 'POST',
contentType: 'text'
}).done(function(ajaxData) {
this.paperData = $.parseJSON(ajaxData);
for (var i = 0; i < this.callbacks.length; i++)
this.callbacks[i](this.paperData);
}.bind(this));
}
};
var loader = new paperDataLoader();
var paper = new Paper();
loader.registerDataLoaded(paper.populate);
loader.loadData();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
It's working for me:
var Paper = function() {};
Paper.prototype = {
populate: function(data) {
data.questions.forEach(function(entry) {
console.log(this);
}.bind(this));
}
};
var paper = new Paper();
paper.populate({ questions: [ 1 ] }); // prints paper object
Maybe you're re-binding paper.populate somewhere?

How to pass value from mainview to subview Backbone.js

Main View
events: {
"click .open-sku-details":"openSkuDetails"
},
openSkuDetails: function(ev) {
var self = this;
var sku_id = $(ev.target).attr('sku_id');
self.skuDetailsModel.set("id",sku_id);
self.skuDetailsModel.fetch({
}).done(function (response) {
});
this.skuDetails = new skuDetailsView({model:self.skuDetailsModel});
return this.skuDetails.render();
}
SubView
var $ = jQuery = require('jquery'),
Backbone = require('backbone'),
Handlebars = require('handlebars'),
_ = require('underscore'),
skuDetailsTemplate = require("../../templates/product/SkuDetails.html"),
skuDetailsModel = require('../../models/product/SkuDetailsModel');
var SkuDetailsView = Backbone.View.extend({
el: ".sku-details-container",
tagName:"div",
initialize: function () {
var self = this;
this.skuDetailsModel = new skuDetailsModel();
this.listenTo(self.skuDetailsModel, 'add', self.render);
this.listenTo(self.skuDetailsModel, 'change', self.render);
self.skuDetailsModel.fetch({
}).done(function (response) {
});
},
render: function () {
var self = this;
this.$el.html(skuDetailsTemplate({
skuDetails: self.skuDetailsModel.toJSON(),
}));
}
});
module.exports = SkuDetailsView;
How do i pass the id value from one view two another?
Pass it via the options argument.
In your main view
this.skuDetails = new skuDetailsView({
model:self.skuDetailsModel,
sku_id: sku_id
});
In your sub view
initialize: function (options) {
var sku_id = options.sku_id;
}
The options object is only available in the initialize function. Store it, for instance with this.sku_id = options.sku_id, if you need it when rendering.
Of course, you could also pass it via the model as an attribute, but if it's just view-related it makes more sense to pass it as an option.

Trying to use jQuery.data() API to cache a function in a jQuery plugin

Please suggest solutions to below mentioned scenario
Background (jQuery Plugin):
$.fn.myplugin = function (action) {
if (actions[action]) {
return actions[action].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof action === 'object' || !action) {
return actions.init.apply(this, arguments);
} else {
$.error('Action ' + action + ' does not exist on myplugin');
return this;
}
};
and variable actions looking like:
var actions = {
init: function (options) {
if (this.length) {
var settings = $.extend({}, $.fn.myplugin.defaults);
return this.each(function () {
if (options) {
settings = $.extend(settings, options);
}
$(this).data('myplugin', new MyPlugin($(this), settings));
});
} else {
throw new Error('unable to init undefined');
}
},
update: function () {
...
},
destroy: function () {
...
}
};
and MyPlugin looking like
function MyPlugin($el, settings) {
var $content = $('.content', $el);
var a = function setup() { ... };
var b = function hold() { ... }
$content.on({
'click': function(e) { ... },
'hover': function(e) { ... }
});
}
I get that I can dump $.cache to console and see what gets associated in .data().
Problem/Suggestions wanted:
If I call the update function like $('myEle').myplugin('update') then I need the update function to change state of the instance of MyPlugin created and cached using .data() API. What are the possible ways to do that?
My current result of $('myEle').data('myplugin') shows MyPlugin{} with nothing between the curly braces.
The problem doesn't have anything to do with jQuery or the data() API, it's due to a misunderstanding about functions, objects and constructors in JavaScript.
This is easy to test inside the JavaScript console in a browser:
> function MyPlugin() { var a = 1; var b = 2; }
undefined
> new MyPlugin()
MyPlugin {}
> function MyPlugin() { this.a = 1; this.b = 2; }
undefined
> new MyPlugin()
MyPlugin {a: 1, b: 2}

"create" KO mapping option for nested objects not used when primary "create" is specified

I have the following:
var CardViewModel = function (data) {
ko.mapping.fromJS(data, {}, this);
this.editing = ko.observable(false);
this.edit = function() {
debugger;
this.editing(true);
};
};
var mapping = {
'cards': {
create: function (options) {
debugger; // Doesn't ever reach this point unless I comment out the create method below
return new CardViewModel(options.data);
}
},
create: function(options) {
//customize at the root level.
var innerModel = ko.mapping.fromJS(options.data);
//debugger;
innerModel.cardCount = ko.computed(function () {
//debugger;
return innerModel.cards().length;
});
return innerModel;
}
};
var SetViewModel = ko.mapping.fromJS(setData, mapping);
debugger;
ko.applyBindings(SetViewModel);
When I run this, the 'cards' method never gets hit, so those edit properties in the CardViewModel aren't available. I can hit that debugger if I comment out the "create" method, but I need both. Any idea what's going on?
'cards' is not a valid Javascript variable name. Try something else without the single quotes.
You will also need to edit your CardViewModel code as this in the inner function refers to the inner function and will not see the knockout observable in the outer function.
var CardViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, this);
this.editing = ko.observable(false);
this.edit = function() {
debugger;
self.editing(true);
};
};

How do I access methods in the scope of an object created with an object factory in Javascript

I have a hard time wrapping my head around variable scope in JS. Is there a way of accessing instance variables of an object created with an object factory similar to the example below?
function Renderer(id, options) {
var id = id;
var options = options;
return {
render: function(selector) {
$(selector).each(function(index) {
this.renderOptions(); //This does not reference the Renderer, but the html element selected by jQuery.
});
},
renderOptions: function() {
console.log(this.options);
}
}
}
var myRenderer = new Renderer('test', [1, 2, 3, 5, 8, 13]);
You just need to keep a named reference to your object, as this gets redefined on every method call and is usually pointing to the wrong context inside callbacks:
var instance = {
render: function(selector) {
$(selector).each(function(index) {
instance.renderOptions();
});
},
...
}
return instance;
Modified code
function Renderer(id, options) {
var id = id;
var options = options;
return {
render: function(selector) {
var self = this;
$(selector).each(function(index) {
self.renderOptions(); // here this is a reference of dom element.
});
},
renderOptions: function() {
console.log(this.options);
}
}
}
Since var options... is within the scope of Renderer, you can simply use options inside of the renderOptions function.
You'll also want to create a reference to this, as other posters mentioned.
function Renderer(id, options) {
var id = id;
var options = options;
return {
render: function(selector) {
var self = this;
$(selector).each(function(index) {
self.renderOptions();
});
},
renderOptions: function() {
console.log(options);
}
}
}
And, if I'm reading the intent of this code correctly, you'll probably want to pass a reference to the element into the renderOptions function:
function Renderer(id, options) {
var id = id;
var options = options;
return {
render: function(selector) {
var self = this;
$(selector).each(function(index) {
self.renderOptions(this);
});
},
renderOptions: function(ele) {
$(ele).css(options); // or whatever you plan to do.
}
}
}

Categories

Resources