I am a newbie to React.js and I found one tricky thing that that stucks me.
I am trying to use one function of a class when I create an instance of that class, but it seems like I am not able to do this.
To be more specific:
I have a class called PersonCalendar and in this class, I have a function called calculatedSalary also, in this class, I have another attribute called salary.
Now I am trying to create an instance of this class by doing the following:
var personCal = new PersonCalender({salary: personCal.calculatedSalary(138)})
I also tried this:
var personCal = new PersonCalender({salary: this.calculatedSalary(138)})
Neither of them are working.
When I execute the code, the code will stop on this line saying that the function is undefined.
Can anyone give me some suggestion on how to deal with tricky situation?
Many thanks
This has nothing to do with React. You simply cannot access something before it exists. Hence you cannot access the function of an instance before the instance exists.
I don't know what PersonCalender is doing or how you are processing its arguments, but it seems like you want to calculate the salary inside the constructor:
function PersonCalender(someNumber) {
this.salary = this.calculatedSalary(someNumber);
}
Or make calculatedSalary static if it does need access to the instance:
PersonCalender.calculatedSalary = function(...) { ...};
var personCal = new PersonCalender({salary: PersonCalender.calculatedSalary(138)})
I recommend to read the MDN article about OOP in JS to make yourself more familiar with this topic.
The 2 lines are wrong for 2 different reasons:
var personCal = new PersonCalender({salary: personCal.calculatedSalary(138)}) wouldn't work since personCal is undefined at this time. Only after the line has executed will it have any value
var personCal = new PersonCalender({salary: this.calculatedSalary(138)}) would probably not work since this is not a PersonCalendar, and doesn't have a calculatedSalary method.
You should probably change your constructor to receive an argument an set salary inside.
Assuming PersonCalendar is a React component: you should pass the salary to your React component and do the calculation internally:
var personCal = new PersonCalender({salary: 138});
var PersonCalendar = React.createClass({
componentDidMount: function() {
this.setState({
computedSalary: this.calculatedSalary(this.props.salary)
});
},
render: function() {
... use this.state.computedSalary
}
});
Others have already pointed out that you can't access properties on a nonexistent object.
Related
I have problem with (most probably) the context of this:
Im new in JS and think more like c++ guy.
Please, see the code:
controller.js :
function CController(){ ...
this.myCanvas = new CCanvas(this);
}
CController.prototype.resize() {...}
canvas.js :
function CCanvas(_mainController){
var controller = _mainController;
}
CCanvas.prototype.myEvent(){
this.controller.resize(); // <--- here!
}
I get the error at mentioned line that controller is undefined.
how can it be corrected?
Most likely not a scoping issue. Assuming your _mainController is actually a controller (which I'll add in the version I'm about to show you), your problem is that in the constructor of CCanvas, you're assigning var controller, not this.controller. This in turn causes controller to be dropped (as var is the keyword for a local variable, after all.
function CCanvas(_mainController){
if (!(_mainController instanceof CController)) throw "Not a controller";
this.controller = _mainController;
}
This should work. And it prevents you from supplying a non-controller.
If you really want to stick to your guns as in the comments and having the variable not on the class but still in lexical scope, do this:
var CCanvas = function(_mainController) {
var outputCCanvas = function(){
};
outputCCanvas.prototype.myEvent = function(){
console.log("Event");
}
return outputCCanvas;
};
The level of indirection on this one is crazy, and you lose a ton of good stuff doing it:
It'll be impossible to do instanceof checks on CCanvas, as each and every instance is generated dynamically every time you call the method
Oh, yeah, your instantiation changes. Now, you're doing new (CCanvas(_mainController))(), since CCanvas is now a method returning a class
I'm searching for alternative ways to call a method defined in Marionette's behaviors from inside a view.
For sure there is the eventproxy but maybe it's more intuitive to call the method directly like:
view.behaviorsMethod();
I could assign it like:
view.method = behavior.method;
I could check for reassignment because it'll maybe lead to unexpected results for others:
view.method = (view.method !== undefined ? view.method : behavior.method);
But this doesn't seem to be an elegant way.
The answer to your question is you can not directly do so, but there is always a way.
you can do it using _.invoke(this._behaviors, 'yourMethodName') but I will discourage using it
since
_behaviors is a private variable of the Marionette.View class and it's name can be changed or it can be dropped in upcoming versions
You will have to set context for the method as _.invoke will not set
the context of the method to proper this.
if you can set the context properly then this will work for you.
as suggested by #ThePaxBisonica in comment
I will suggest you to go with a mixin pattern from which you can extend both your behavior and view and you will not have to set any context and do not have to worry about the _behavior private variable
as
var mixin = {
behaviorMethodWhichYouWantToCallFromView: function(){
alert("mixin method");
}
}
var behavior = mn.behavior.extend(_.extend(mixin, {
//actual behavior code remove the method as behavior will get it from mixin
}))
var view = mn.view.extend(_.extend(mixin, {
//actual view code remove the method as behavior will get it from mixin
}))
Hope it helps.
I know this is bit long approach.
A factory class isn't the end product I am looking for but it is basically my problem boiled down. I am looking for a class we'll call foo that can be passed in another class bar as a parameter to its constructor such that when later calling fooInstance.create() it returns a new instance of bar. I can easily figure out how to make a fooInstance.create(bar) create an instance of bar. But that isn't what I need. I need each instance of the factory class to create a specific type of object. so:
fooCar = new foo(Car);
fooTruck = new foo(Truck);
myCar = fooCar.create();
myTruck = fooTruck.create();
This would be something easily handled by Generics in c#. I either get errors or I end up screwing with the prototype of foo which then changes the type created by all instances of foo.
Just return a regular object from your factory, with just one create method:
function foo (Constructor) {
return {
create: function () {
return new Constructor();
}
};
}
Check out the fiddle: http://jsfiddle.net/fR5Gz/1/
Thanks to everyone for your help. #missingno I thought the details of what I was trying to do would cloud the issue but now I think it was the other way around. I'll know better for next time. I wasn't looking for type safety but without it maybe there is no reason to do it this way. Maybe I'm stuck in classical thinking.
I actually figured it out myself this morning because I didn't get any email alerts on this thread like I was supposed to so I thought there weren't any replies yet. This is my solution (relevant code only):
ORM.Repository = (function (){
function Repository(ob){
this.construct = ob;
};
//bunch of other generic repository stuff deleted
Repository.prototype.make = function () { return new this.construct();}
return Repository;
})();
Usage:
var carRepository = new ORM.Repository(car);
var personRepository = new ORM.Repository(person);
var myCar = carRepository.make();
var user = personRepository.make();
I've got a browser addon I've been maintaining for 5 years, and I'd like to share some common code between the Firefox and Chrome versions.
I decided to go with the Javascript Module Pattern, and I'm running into a problem with, for example, loading browser-specific preferences, saving data, and other browser-dependent stuff.
What I'd like to do is have the shared code reference virtual, overrideable methods that could be implemented in the derived, browser-specific submodules.
Here's a quick example of what I've got so far, that I've tried in the Firebug console, using the Tight Augmentation method from the article I referenced:
var core = (function(core)
{
// PRIVATE METHODS
var over = function(){ return "core"; };
var foo = function() {
console.log(over());
};
// PUBLIC METHODS
core.over = over;
core.foo = foo;
return core;
}(core = core || {}));
var ff_specific = (function(base)
{
var old_over = base.over;
base.over = function() { return "ff_specific"; };
return base;
}(core));
core.foo();
ff_specific.foo();
Unfortunately, both calls to foo() seem to print "core", so I think I've got a fundamental misunderstanding of something.
Essentially, I'm wanting to be able to call:
get_preference(key)
set_preference(key, value)
load_data(key)
save_data(key, value)
and have each browser do their own thing. Is this possible? Is there a better way to do it?
In javascript functions have "lexical scope". This means that functions create their environment - scope when they are defined, not when they are executed. That's why you can't substitute "over" function later:
var over = function(){ return "core"; };
var foo = function() {
console.log(over());
};
//this closure over "over" function cannot be changed later
Furthermore you are "saying" that "over" should be private method of "core" and "ff_specific" should somehow extend "core" and change it (in this case the private method which is not intended to be overridden by design)
you never override your call to foo in the ff_specific code, and it refers directly to the private function over() (which never gets overridden), not to the function core.over() (which does).
The way to solve it based on your use case is to change the call to over() to be a call to core.over().
That said, you're really confusing yourself by reusing the names of things so much, imo. Maybe that's just for the example code. I'm also not convinced that you need to pass in core to the base function (just to the children).
Thanks for your help. I'd forgotten I couldn't reassign closures after they were defined. I did figure out a solution.
Part of the problem was just blindly following the example code from the article, which meant that the anonymous function to build the module was being called immediately (the reusing of names Paul mentioned). Not being able to reassign closures, even ones that I specifically made public, meant I couldn't even later pass it an object that would have its own methods, then check for them.
Here's what I wound up doing, and appears to work very well:
var ff_prefs = (function(ff_prefs)
{
ff_prefs.foo = function() { return "ff_prefs browser specific"; };
return ff_prefs;
}({}));
var chrome_prefs = (function(chrome_prefs)
{
chrome_prefs.foo = function() { return "chrome_prefs browser specific"; };
return chrome_prefs;
}({}));
var test_module = function(extern)
{
var test_module = {};
var talk = function() {
if(extern.foo)
{
console.log(extern.foo());
}
else
{
console.log("No external function!");
}
};
test_module.talk = talk;
return test_module;
};
var test_module_ff = new test_module(ff_prefs);
var test_module_chrome = new test_module(chrome_prefs);
var test_module_none = new test_module({});
test_module_ff.talk();
test_module_chrome.talk();
test_module_none.talk();
Before, it was running itself, then when the extension started, it would call an init() function, which it can still do. It's just no longer an anonymous function.
Please bear with me as I'm new to JS and am having trouble implementing some things with Meteor. I implemented a class in JavaScript using
function Class() {
this.property = 0
this.method = function () {
return "method called"
}
}
I made a new Meteor Collection bu using new Meteor.collection and successfully retrieved the data on the client and can display Class.property in the html template. However, I am unable to access Class.method and was wondering if there's any way to make this happen and if using Meteor.methods to define functions that take the Class instance as input is the best way to go.
For anyone still looking at this, the reason the code doesn't work is because mongodb stores documents as bson. bson, just like json, does not support functions (http://bsonspec.org) so when the above class is saved by meteor into mongo, the method is not saved as part of the document.
There is no easy elegant solution I'm aware of. I have the same issue. In order to utilise the class method you would need to instantiate the class each time you needed it, which you could implement as part of a database model.
This is not really an answer but in meteor's package manager you can add libraries like backbone.js which gives you models, collection and views and a nice router which I find very handy when making meteor apps. Backbone works well with jQuery.
My other suggestion is using a library like Mootools which unlike jQuery doesn't try to change the way you write javascript but enhancing the experience of making object oriented javascript. (see: jqueryvsmootools). With mootools you can can make a class the following way...
var MyClass = new Class({
'Implements': [Options],
//default options
'options': {
'foo': null
},
'initialize': function(options) {
this.foo = options.foo;
},
'bar' : function() {
return this.foo;
}
});
var blub = new MyClass({'foo': 'Hello World'});
blub.bar(); // "Hello World"
I was looking to do the same thing.
I found a function called "transform" that is called when getting something from a meteor collection. You can use it to add a function to a meteor object just as you require.
Here is an example of adding an "endDate" function and "remaining" functions to a meteor object
Products = new Meteor.Collection("Products", {
transform: function (doc) {
doc.endDate = function () {
// SugarJS gives us minutesAfter() which gives us a nice syntax for
// creating new Date objects
// http://sugarjs.com/api/Number/unitAfter
return ((25).minutesAfter(this.startDate));
};
doc.remaining = function () {
return this.endDate().getTime() - Date.now();
};
return doc;
}
});
Read more here:
http://www.okgrow.com/posts/2014/05/19/meteor-transform/
This approach worked beautifully for me:
http://www.okgrow.com/posts/2014/05/19/meteor-transform/
I don't know anything about Meteor, but I see a problem with your code. You're missing a semi-colon after:
this.property = 0
Without that semi-colon, the javascript interpreter will not execute the this.method assignment.