Use the function inside another function as the constructor in javascript - javascript

I have the function structured this way because I need to inject it as a angularjs Factory. however, when I use it stand along to create a test for it, I encounter difficutly. I can NOT reference the ItemModel inside the ItemModelGenerator! I can not create an instance of it by using it as a constructor! I tried many many ways use keyword of new or invoke both, invoke either, pass arguments in bot or either, none of them works. I am confused...
Is this possible to somehow use this ItemModelGenerator as a constructor for another var? or, let say use the ItemModel inside it to generate, but in a condition that of course, the var has to be outside of ItemModelGenerator, because it is a factory.
I tried:
var Service = new ItemModelGenerator();
Service.ItemModel();
new ItemModelGenerator().ItemMode();
new ItemModelGenerator.ItemMode();
..etc
BTW, it does work as a angularjs factory injection, its tested.
Thanks
'use strict';
function ItemModelGenerator() {
function ItemModel(inputItem) {
var defaults = {
id:'na',
name:'na'
};
var location = inputItem ? inputItem : { defaults };
this.id = location.id;
this.name = location.itemName ? location.itemName : location.name;
this.itemIsReal = this.isReal(this.id);
}
ItemModel.prototype.isReal = function(id) {
return id !== false ? true : false;
};
return ItemModel;
}

You are returning ItemModel from ItemModelGenerator when you call ItemModelGenerator. So what you get back is an ItemModel:
var ItemModel = ItemModelGenerator();
var instance = new ItemModel();
alert(instance.name);

You are returning the constructor function as the result of your wrapping function. Try:
var Service = new ItemModelGenerator()();

Related

Pick the prototype to avoid if-else

The below code snippet I found on one blogs to avoid if-else statement. This code is very modular and can be easily extended. But I am not able to get this to work.
CatModel.prototype.makeWords = function () {
console.log('inside catmodel')
this.setWord('meow')
this.sayIt()
}
DogModel.prototype.makeWords = function () {
console.log('inside dogmodel')
this.setWord('bark')
this.saySomething()
}
// somewhere else
var makeWords = function (type) {
var model = namespace[type + 'Model']
model.makeWords()
}
makeWords('cat')
Presumably the CatModel and DogModel functions are declared somewhere and setWord and sayIt are also set up on their prototype object.
You'd need to put CatModel and DogModel in an object and refer to it from namespace (which I'd recommend not calling namespace):
var namespace = {
CatModel: CatModel,
DogModel: DogModel
};
Then when creating an instance, use new (you always use new with constructor functions). I'd also put the () on the call even though strictly speaking they're optional if you don't have parameters to pass:
var makeWords = function (type) {
var model = new namespace[type + 'Model']()
// ---------^^^--------------------------^^
model.makeWords()
}

How do I properly extend an angularJS factory

If I have some base factory that has a bunch of methods I want to extend to a bunch of other factories such as
function TestFactory() {
var service = {};
service.name = null
service.lastname = null
service.screech = function() {
alert(service.name + service.lastname)
}
return service;
}
And I want to extend that functionality to another service such as
function NewFactory() {
var service = angular.copy(TestFactory)
service.name = 'Cool'
service.lastname = 'Guy'
return service;
}
I would expect NewFactory.screech() to alert "CoolGuy", but it appears it is calling the screech method in the scope of the original TestFactory where name and lastname are null.
How can I accomplish this pattern?
I have also tried using angular.extend but had the same result.
Change service to this inside the definition of service.screech:
service.screech = function() {
alert(this.name + this.lastname)
}
It didn't work because when you referred to service inside the function, due to closure, service was hard-coded to the service object at the time of definition. The reference was not updated when you made the copy. this, on the other hand, is dynamic and always refers the the object that the function is being called on, e.g. with service.screech().
Also, you need to set service to a copy of the object resulting from the TestFactory, not to a copy of the TestFactory itself:
var service = angular.copy(TestFactory())
Change service variable to this. Closure will bind the service variable to the function instead service should be using scope of the object so that it can be inherited.
All functions inside the factory should be using this to refer the object.
function TestFactory() {
var service = {};
service.name = null
service.lastname = null
service.screech = function() {
alert(this.name + this.lastname)
}
return service;
}
Also change the following code to return the factory object not the Factory Function
function NewFactory() {
var service = angular.copy(TestFactory())
service.name = 'Cool'
service.lastname = 'Guy'
return service;
}

Returning variables in Angular JS factories?

I have the following code...
spa.factory("linkService", function() {
var currentLink = null;
return {currentLink:};
});
var cssController = spa.controller("cssController", function(linkService, currentLink) {
this.fileName = linkService.currentLink;
});
var navigationController = spa.controller("navigationController", function(linkService, currentLink) {
this.setLink = function(setValue) {
linkService.currentLink = setValue;
};
this.checkCurrent = function(checkValue) {
return linkService.currentLink == checkValue;
}
});
I've created this code from a snippet I wrote in another question, and fixed the things I was told were wrong with my first attempt. That question can be found here. This is a more specific case, and is therefore not a duplicate.
After checking the console, I believe the problem with this script lies in the factory.
The attempted function of the linkService factory is to provide a variable, currentLink which can be accessed and changed dynamically by more than one controller. The variable inside the factory I believe should be accessed by injecting the factory as a dependency into the controller function, as well as the variable.
What is the issue here?
Your linkService factory returns an object with the slot 'currentLink' and its value undefined.
You have to return the variable directly or you can return an object which references your variable or you can return an object which includes getter and setters to your variables.
The last variant has the advantage of having a nice interface defined in place.
Your code will be better readable.
spa.factory("linkService", function() {
var currentLink = "/myLink.html";
/* accessor for currentLink */
var getLink = function(){
return currentLink;
};
return {get: getLink}; // service interface
});
--- update
Some more details:
currentLink is the variable which should be accessed and which is present in the context of the linkService only
the factory returns the object "{get: getLink}" as the service. So if a controller injects the linkService, it gets this object.
Using linkService.get(); you can access the variable currentLink.
getLink is a local function only. It serves as an accessor.
The factory should return objects/methods as such:
spa.factory("linkService", function() {
var linkServiceFactory = {};
var _currentLink = null;
linkServiceFactory.currentLink = _currentLink;
return linkServiceFactory;
});
In the controller to inject it with the factory name to get its sub-parts.:
var cssController = spa.controller("cssController", function(linkService) {
this.fileName = linkService.currentLink;
});
Similarly,
var navigationController = spa.controller("navigationController", function(linkService) {
// other code.
});

methods as properties of an object in javascript

i need to create a javascript function with a private variable that has setter and getter methods. i tried:
function createSecretHolder(secret) {
this._secret = secret;
var getSecret = function(){
return this._secret;
}
var setSecret = function(secret){
this._secret = secret;
}
}
and a version with:
this.getSecret = function()...
and
this.seSecret = function()...
it is not passing the test suite on code wars. something like
var obj = createSecretHolder(secret);
obj.getSecret();
obj.setSecret(newSecret);
and others which are hidden. I get an error TypeError: Cannot read property 'getSecret' of undefined and another cannot call method setSecret
createSecretHolder() doesn't return a value so createSecretHolder(secret) returns undefined.
So var obj = createSecretHolder(secret); sets obj to undefined. Hence the error "Cannot read property 'getSecret' of undefined" when you try to access obj.getSecret().
Even if it returned an object, you have declared getSecret using var inside a function. Variables described that way are scoped to the function. So when you try to access it outside the function, as you do in obj.getSecret(), that won't work.
You also seem to misunderstand how this works. It will not create a private variable.
There are a number of ways to do this. Here's one:
function createSecretHolder(mySecret) {
var secret = mySecret;
return {
getSecret: function(){
return secret;
},
setSecret: function (mySecret){
secret = mySecret;
}
};
}
When you use a Constructor to create an object you need to use the new keyword.
Inside your constructor you set a property with this._secret = secret. This is accessible from outside. It should be var _secret = secret.
Also you create local functions inside your object to get/set _secret. They should be methods of the object. See below.
// object constructor
function createSecretHolder(secret) {
// local variable. it isn't accessible from outside
var _secret = secret;
// method to get the secret
this.getSecret = function() {
return _secret;
}
// method to set the secret
this.setSecret = function(secret){
_secret = secret;
}
}
// create new "createSecretHolder" object
var secret = new createSecretHolder("secret 1");
secret.getSecret(); // returns "secret 1"
secret.setSecret("secret 2");
secret.getSecret(); // returns "secret 2"
Look into prototypejs OO programming.
If you can't use prototypejs because of jQuery conflicts, there is a version of it only with OO support here: https://github.com/Prescia/Prototypejslt
Your could would look like:
createSecretHolder= Class.create();
createSecretHolder.prototype = {
_secret: 0,
othervariable: true,
initialize: function(inSecret) { // this is the constructor on prototypejs
this._secret = inSecret;
}
}
// Create instance:
var myInstance = new createSecretHolder(5);
// so this should alert "5":
alert(myInstance._secret);
I can't live without prototypejs Object Orientation, so I often use this light version that won't conflict with other stuff

How to create a javascript class or module, I need 2 instances on a page

I am wrapping common javascript functions that will work on elements on a page.
My page has 2 of these elements (textareas), so I will need to create 2 instances and then I want to do this:
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init("ta1");
textArea2.init("ta2");
I tried doing this the module pattern way, but I'm confused how I can create 2 seperate instances of it?
var MYMODULE = function() {
var _init = function(ta) {
// ..
}
return {
init: function(ta) {
_init(ta);
}
};
}();
Use a constructor function:
function SomeClass(id) {
this.id = id;
// ...
}
Usage:
var textArea1 = new SomeClass("ta1");
var textArea2 = new SomeClass("ta2");
You can put methods for the class in the prototype for the function. Example:
SomeClass.prototype = {
getValue: function() { return document.getElementById(this.id).value; }
};
Usage:
var text = testArea1.getValue();
Using your specific example, you could just MYModule twice, but it's a weird pattern that doesn't seem to do a whole lot.
Simple example how instantiation works:
function SomeClass() {
// constructor
}
SomeClass.prototype.init = function(ta) {
// ..
}
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init('ta1');
textArea2.init('ta2');
But regardless, you may like Backbone.js
Your MYMODULE idea will work fine. As above and then
MYMODULE.init("ta1");
MYMODULE.init("ta2");
This line here will not care it is called with two different parameters
var _init = function(ta) {
// ..
}
It is just a place to hold a function. The real question is what is inside that function.
For example if it works with ta in some standard way (attaches event handlers, does some styling.. ) then it will not be a problem. The issue will be if you use MYMODULE local variables and expect to have more than one of them. You only have one MYMODULE so local variables will be shared with this design. This might be what you want. I'm not sure.
This pattern can work fine for a control passed in having special data all itself. The best way to do this -- since you are using jQuery is with the data function... thus the code could look like:
var _init = function(ta) {
jQuery.data(ta,"foo", 10);
// etc
}

Categories