Javascript: Modify array directly only within its own function - javascript

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();
};
}

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).

Pass array to function or make sure it is within scope?

I am trying to sort an array using an onclick function, and I'm getting the error obj is not defined. Maybe it's not within the scope?
js file:
function pageLoad() {
var url = API_URL + "/api/Ticket/GetTickets";
var data = Ajax.getData(url);
var obj = [];
var tickets = JSON.parse(data.JsonResult);
obj.Tickets = tickets;
Tickets.Data = obj;
var viewModel = {
theObject: [obj.Tickets]
};
ko.applyBindings(viewModel);
}
function SortColumn(column) {
obj.Tickets.column.sort();
}
Yes, it is a scoping issue. Move your obj declaration outside the functions, that way it will be visible to both.
Something like this:
var obj = []; // <-- move here
function pageLoad() {
var url = API_URL + "/api/Ticket/GetTickets";
var data = Ajax.getData(url);
var tickets = JSON.parse(data.JsonResult);
obj.Tickets = tickets;
Tickets.Data = obj;
var viewModel = {
theObject: [obj.Tickets]
};
ko.applyBindings(viewModel);
}
function SortColumn(column) {
obj.Tickets.column.sort();
}
EDIT:
I'm not sure if you're aware, but [] is shorthand for an Array. If what you're trying to create is an Object, then you should change your assignment to var obj = {};
Of course obj is not within scope. It's not in global scope and you aren't passing it into the function.
you can use #smaili's answer as a fix or you can pass obj to the function.
function sortColumn(obj, column) {
obj.Tickets.column.sort();
return obj;
}
Then you'll call it with both arguments.
var sortedObj = sortColumn(obj, column);
Further explanation of scoping in Javascript

Proper way to reference js property from another property

I am working on an angular project and I have a factory providing some global database methods. I tested this in a jsfiddle and it works, but I want to know if it is the right way to do it.
So here's the jsFiddle.
function DB () {
return {
newRecord: function () {
//create new record
var id = 3;
//this is the part I am wondering about
//is it ok to use this in this way??
this.setCurrentRecordId(id);
},
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
Like I said, it seems to be working; just want to know if there is a better way or not.
Thanks!
The "standard" way of using constructor functions in JavaScript would be the following:
function DB () {
this.newRecord = function () {
var id = 3;
// yes, since you invoked the DB constructor using
// using the new keyword, this will be pointing to
// the created instance
this.setCurrentRecordId(id);
};
this.setCurrentRecordId = function (id) {
alert('setting id');
return localStorage.setItem('id', id);
};
this.getCurrentRecordId = function () {
return localStorage.getItem('id');
};
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
In case you need to reference the instance in a callback or some other situation in which context is lost there are two common patterns to deal with this.
Either store a reference to this (considered "ugly" by some, very convenient though):
function Ctor(){
var self = this;
this.getSomething = function(id){
asyncThing(id).then(function(result){
// here, `this` will refer to the global object
self.doSomethingWith(result);
});
};
this.doSomethingWith = function(result){
// do something
};
}
Or use .bind() to create a new function with a predefined context:
function Ctor(){
this.getSomething = function(id){
var processResult = function(arg){
this.doSomethingWith(arg);
}.bind(this); // bind sets the function's context no matter where you'll use it
asyncThing(id).then(processResult);
};
this.doSomethingWith = function(result){
// do something
};
}
Since you are using localstorage, there isn't any problem.
function DB () {
return {
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbstuff = new DB();
dbstuff.setCurrentRecordId(3);
dbstuff.getCurrentRecordId() // 3

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 module pattern, nested functions, and sub modules

I am trying to wrap my head around javascript modules, but I'm unsure how to split up a module into further sub modules. I have read that nested functions are not really a great idea, due to performance, so how do I break up a function in a module? For example, lets say I have the following module:
var Editor = {};
Editor.build = (function () {
var x = 100;
return {
bigFunction: function () {
// This is where I need to define a couple smaller functions
// should I create a new module for bigFunction? If so, should it be nested in Editor.build somehow?
}
};
})();
bigFunction is only related to Editor.build. Should I attach the smaller functions that make up bigFunction to the prototype bigFunction object? I'm not even sure if that would make sense.
var Editor = {};
Editor.build = (function () {
var x = 100;
return {
bigFunction: function () {
bigFunction.smallFunction();
bigFunction.prototype.smallFunction = function(){ /*do something */ };
// not sure if this even makes sense
}
};
})();
Can someone please throw me in the right direction here? There is so much misleading information online, and would just like a definite guide on how to deal with this sort of modularization.
Thank you.
Here is a snippet I use to make names for an input:
var dynamicCounter = 0;
//custom dropdown names
var createContainerNames = function () {
function Names() {
this.id = "Tasks_" + dynamicCounter + "__ContainerId";
this.name = "Tasks[" + dynamicCounter + "].ContainerId";
this.parent = "task" + dynamicCounter + "Container";
}
Names.prototype = { constructor: Names };
return function () { return new Names(); };
} ();
And then I use it:
var createdNames = createContainerNames();
var createdId = createdNames.id;
dynamicCounter++;
var differentNames = createContainerNames();
var differentId = differentNames.id;
Another approach would be to do this:
var NameModule = function(){
//"private" namemodule variables
var priv1 = "Hello";
//"private namemodule methods
function privMethod1(){
//TODO: implement
}
//"public namemodule variables
var pub1 = "Welcome";
//"public" namemodule methods
function PubMethod(){
//TODO: pub
}
return {
pub1 : pub1,
PubMethod: PubMethod
};
and then to use it
var myPubMethod = new NameModule();
myPubMethod.PubMethod();
var pubVar = myPubMethod.pub1;
EDIT
You could also take this approach:
var mod = function(){
this.modArray = [];
};
mod.prototype = {
//private variables
modId: null,
//public method
AddToArray: function (obj) {
this.modArray.push(obj);
}
}

Categories

Resources