Create clones of root model observable array within objects - javascript

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

Related

private object not setting data

Hi I'm trying to implement a LinkedList in Javascript. When i assign a value to my node it doesn't seem to store it when I use my getter. For example:
var Node =function() {
var _data;
var _next ={};
var that = this;
that.getData = function() {
return _data;
};
that.setData = function(data) {
that._data = data;
};
that.getNext = function() {
return _next;
};
that.setNext = function(next) {
that._next = next;
};
return that;
};
Will not work with:
var nodeObj = new Node();
nodeObj.setData("hello");
console.log(nodeObj.getData());
_data is not the same as that._data, you must do this:
that.getData = function() {
return that._data;
};
OR you could do this instead:
that.setData = function(data) {
_data = data;
};
the benefit of the second approach being that you're simulating a private variable (because you cannot do nodeObj._data in the second case but you can in the first)
also var that = this; is unnecessary, you can simply do this._data in this case.
For your case here, you can assume that if you're calling a function like yourObject.someFunction(), then within someFunction the value of this equals yourObject. (And this isn't always true in javascript but since you're starting off you should think about it this way for now. If you pass a function to another function as a variable and then call it then this wouldn't be the case).

Extend the properties returned by a function?

I'm a JS beginner. I have defined a function on my Backbone model as follows.
myFunction: function () {
return {
firstAttr: this.model.get('value-attribute')
};
}
It is available to me as this.myFunction.
From somewhere else in the code, I want to extend this.myFunction to return another attribute. In other words, I'd like it to return a dict with two attributes: { firstAttr: 'something', secondAttr: true }.
How can I do this?
I've tried:
this.myFunction().secondAttr = true;
but I know that's the wrong thing to do.
Assuming your model prototype looks like
var MyModel = Backbone.Model.extend({
myFunction: function () {
return {
// I assume you work directly on a model
// the principle would be the same with a wrapper object
firstAttr: this.get('value-attribute')
};
}
});
you can either mask your method on a model by model basis like this:
var m = new MyModel({'value-attribute': 'attr, the first'});
console.log(m.myFunction());
m.myFunction = function () {
var res = MyModel.prototype.myFunction.call(this);
res.secondAttr = true;
return res;
};
console.log(m.myFunction());
See http://jsfiddle.net/V8zt2/ for a demo
Or dynamically modify your prototype to alter all instances :
var f = MyModel.prototype.myFunction;
MyModel.prototype.myFunction = function () {
var res = f.call(this);
res.secondAttr = true;
return res;
};
var m = new MyModel({'value-attribute': 'attr, the first'});
console.log(m.myFunction());
http://jsfiddle.net/V8zt2/1/
How about modifying your myFunction to :
myFunction : function () {
var i,
obj = {};
for (i=0; i< arguments.length;i++){
obj['attribute'+(i+1)] = this.model.get(arguments[i]);
}
return obj;
}
This way you can send keys of model, that you want to be in the returned object as arguments to myFunction.

Javascript: Modify array directly only within its own function

I have a very simple function:
var errorsViewModel = function () {
var self = this;
var _errors = ko.observableArray([]);
self.get = function () {
return _errors;
};
self.insert = function ( error ) {
_errors.push(error);
};
}
What I want to acomplish is make _errors array modifiable directly only within its own function. That is users from outside can get the array for reading through the get method and insert itsert items only through the insert method.
But not to be able to do something like this:
var err = new errorsViewModel();
var array = err.get();
array.push('item');
Instead use the errorsViewModel interface :
err.insert('some error');
Is that possible?
Just copy the returned array:
self.get = function () {
return _errors.slice(0);
};
That way, when get is called, the caller can make changes to it if they want - but it won't modify the original.
To make sure that your array isn't accessible from outside your scope I would suggest that you expose the array via a ko.computed and then notify it's listeners on an insert.
var errorsViewModel = function () {
var self = this;
var _errors = [];
self.errors = ko.computed(function () {
return self.get();
});
self.get = function () {
return _errors.splice(0);
};
self.insert = function ( error ) {
_errors.push(error);
self.errors.valueHasMutated();
};
}

No access to this in prototype

Why does this alert as undefined in the code below?
http://jsfiddle.net/7kwXd/6/
var testObj = {};
testObj.aMethod = function() {
this.testVar = "thing"
alert(this.anObject.dimension1);
alert(this.anObject.dimension2);
};
testObj.aMethod.prototype.anObject = {
dimension1 : this.testVar,
dimension2 : "thing2"
};
var testing = new testObj.aMethod();
You are creating an object ({dimension1: this.testVar, dimension2: "thing2"}) without any object context. What is this supposed to be at that time? After that, you are assigning it to testObj.aMethod.prototype.anObject.
You can use code like this to achieve what you want:
var testObj = {};
testObj.aMethod = function() {
this.testVar = "thing"
alert(this.anObject.dimension1);
alert(this.anObject.dimension2);
};
testObj.aMethod.prototype.getAnObject = function() {
return {
dimension1 : this.testVar,
dimension2 : "thing2"
};
};
var testing = new testObj.aMethod();
And then access that object with testing.getAnObject().dimension1.

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