How to pass value from mainview to subview Backbone.js - javascript

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.

Related

Add new collection to parent view from child view

I have a parent view (which is for car engines) that contains a child view which displays a list of options. One of the options is to add a new collection to the parent view.
My child view init function looks like this:
initialize: function (engine) {
this.engine = engine; //parent object
this.valves = engine.valves; //may or may not be empty
};
Then I have this method that adds the collection(valves) when a button is pressed:
addPerformanceValves: function() {
var self = this;
if (this.valves.lentgh == 0) {
this.valves = new ValveCollection();
this.valves.url = function() {
return '/api/engines/auto/performance/parts/' + self.id + '/valves';
}
}
this.$('.performanceParts').show();
}
So now that I created the new collection, how do I add it to the parent?
There are multiple ways to achieve that.
Passing the parent object down the hierarchy
Like you're already doing, you could call a function from the parent object to pass the new collection.
var Child = Backbone.View.extend({
initialize: function(options) {
options = options || {};
this.engine = options.engine; //parent object
this.valves = engine.valves; //may or may not be empty
},
addPerformanceValves: function() {
var self = this;
if (this.valves.lentgh == 0) {
this.valves = new ValveCollection();
this.valves.url = function() {
return '/api/engines/auto/performance/parts/' + self.id + '/valves';
}
// call the parent
this.engine.addNewCollection(this.valves);
}
this.$('.performanceParts').show();
}
});
var Parent = Backbone.View.extend({
addNewCollection: function(collection) {
// do what you want with the collection
this.newCollection = collection;
}
});
Triggering events
One way to avoid strong coupling is to trigger events from the child view, to which the parent is listening.
var Child = Backbone.View.extend({
initialize: function(options) {
options = options || {};
this.valves = options.valves; //may or may not be empty
},
addPerformanceValves: function() {
var self = this;
if (this.valves.lentgh == 0) {
this.valves = new ValveCollection();
this.valves.url = function() {
return '/api/engines/auto/performance/parts/' + self.id + '/valves';
}
// call the parent
this.trigger('child:collection', this.valves);
}
this.$('.performanceParts').show();
}
});
var Parent = Backbone.View.extend({
initialize: function() {
this.child = new Child({ valves: this.valves });
// listen to child view events
this.listenTo(this.child, 'child:collection', this.addNewCollection);
},
addNewCollection: function(collection) {
// do what you want with the collection
this.newCollection = collection;
}
});
Then, the child view only has what it needs and nothing more. It help to keep responsibilities at the right place.

Create clones of root model observable array within objects

I'm trying to create a clone of an self.tags observable array within each line object I create, where self.field_name reflects the value but self.field_value is an independant value for each Line object. I'm getting a bit mixed up how to do this, do I need a manual subscription to only update one of the values?
function TechnicalViewModel(){
var root = this;
function Tag(data){
var self = this;
self.field_name = ko.observable(data.field_name)
self.field_value = ko.observable(data.field_value)
}
root.tags = ko.observableArray([
{
new Tag(field_name : 'tag_name_1', field_value : '',
new Tag(field_name : 'tag_name_2', field_value : '',
new Tag(field_name : 'tag_name_3', field_value : ''
}])
function Line(data) {
var self = this;
self.tags = root.tags;
}
root.lines = ko.observableArray([]))
}
Ko mapping pluggin can be used for cloning.
//Use this function as constructor function
function Tag(data) {
var self = this;
self.field_name = data.field_name;
self.field_value = data.field_value;
}
function Line(data) {
var self = this;
self.tags = ko.mapping.fromJS(root.tags); //mapping pluggin will make a copy of tags
self.remove = function (data) {
self.tags.remove(data);
}
self.add = function () {
self.tags.push(new Tag({
field_name: 'new tag_name',
field_value: ''
}));
}
}
Fiddle Demo

Accessing one javascript class inside another

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.

"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