JS/Reactjs - accessing a parameter passed from top level function - javascript

Box.expandBox(id);
var Box= (function(){
return {
expandBox: function(id) {
console.log('inside expandBox: ' + id);
ReactDOM.render( React.createElement(this.pBox(id)), document.getElementById('activate'))
},
pBox: function(id) {
console.log('inside pBox: '+ id);
return React.createClass({
getInitialState: function(id) {
console.log('inside getInitialState: '+ id);
return {
person_id: id
}
},
........
Trying to assign the state person_id to the id that is being passed externally. I got as far as outputting the data inside pBox but data is lost inside React's child function. I've tried doing the var self = this assignment but to no avail. I'm am lost at the scope when it comes to JS.

Currently, the id is shadowed by the argument of getInitialState(id) and becoming undefined because getInitialState is called without any arguments.
So, remove the argument and you can use the id provided in the pBox method in getInitialState().
pBox: function(id) {
console.log('inside pBox: '+ id);
return React.createClass({
getInitialState: function() {
console.log('inside getInitialState: '+ id);
return {
person_id: id
}
},

The function getInitialState is expecting an argument for id. If it does not receive one, there still is an id in that local scope but it contains undefined. As mentioned by #Shuhei, removing that argument from the function or giving it a different name will allow you to access the id on a higher scope.
For readability's sake, I recommend you separate React.CreateClass into another function.
Your new function would look something like this:
function foo(id){
console.log('inside pBox: '+ id);
return React.CreateClass({...}) //Same code you had before
}
And your code would look something like this:
Box.expandBox(id);
var Box= (function(){
return {
expandBox: function(id) {
console.log('inside expandBox: ' + id);
ReactDOM.render( React.createElement(this.pBox(id)), document.getElementById('activate'))
},
pBox: foo
....

Related

React.js click event inside map function [duplicate]

I used this article as an example (React way), but it is not working for me. Please point me to my mistake, as I can't understand what's wrong.
This is the error I see:
Uncaught TypeError: this.props.onClick is not a function
Here is my code:
// PARENT
var SendDocModal = React.createClass({
getInitialState: function() {
return {tagList: []};
},
render: function() {
return (
<div>
{
this.state.tagList.map(function(item) {
return (
<TagItem nameProp={item.Name} idProp={item.Id} onClick={this.HandleRemove}/>
)
})
}
</div>
)
},
HandleRemove: function(c) {
console.log('On REMOVE = ', c);
}
});
// CHILD
var TagItem = React.createClass({
render: function() {
return (
<span className="react-tagsinput-tag">
<span>{this.props.nameProp}</span>
<a className='react-tagsinput-remove' onClick={this.HandleRemove}></a>
</span>
)
},
HandleRemove: function() {
this.props.onClick(this);
}
});
Thanks in advance!
The issue is that this inside the map callback does not refer to the React component, hence this.HandleRemove is undefined.
You can set the this value explicitly by passing a second argument to map:
this.state.tagList.map(function() {...}, this);
Now this inside the callback refers to the same value as this outside the callback, namely the SendDocModal instance.
This has nothing to do with React, it's just how JavaScript works. See How to access the correct `this` context inside a callback? for more info and other solutions.
Try the following:
var SendDocModal = React.createClass({
getInitialState: function() {
var item = {};
item.Name = 'First';
item.Id = 123;
var item2 = {};
item2.Name = 'Second';
item2.Id = 123456;
return {tagList: [item,item2]};
},
HandleRemove: function(c){
console.log('On REMOVE = ', c);
},
render: function() {
return (<div>
{this.state.tagList.map(function(item){
return(
<TagItem nameProp={item.Name} idProp={item.Id} key={item.Id} click={this.HandleRemove}/>
)}, this)}
</div>
)
}
});
// CHILD
var TagItem = React.createClass({
handleClick: function(nameProp)
{
this.props.click(nameProp);
},
render: function(){
return(
<span className="react-tagsinput-tag" ><span onClick={this.handleClick.bind(this, this.props.nameProp)}>{this.props.nameProp}</span><a className='react-tagsinput-remove' ></a></span>
)
}
});
Few changes:
Added 'this' after the tagList mapping. To be honest I am not entirely sure why - perhaps a more experienced programmer can tell us.
Added a key to each TagItem. This is recommended and an the console will inform you that you should do this so that if the state changes, React can track each item accordingly.
The click is passed through the props. See React js - having problems creating a todo list

backbone marionette pass variable to view method

I have simple situation and can't understand why variable that I pass to function always undefined.
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
I expect that it equals '4'. In next step I want to pass "data" but now I realize that I can't pass anything.
Since you're invoking renderProgress on the return of $.getJSON you can simply provide the function reference to the done()method of the returned jQuery Promise. Your code would look like this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(this.renderProgress);
},
renderProgress: function (data) {
alert(data);
...
},
...
});
If you'll need the view context inside renderProgress (like, for example, to refer to a view property), then provide done() a version of renderProgress that's bound to the view context:
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(_.bind(this.renderProgress, this));
where _.bind is an UnderscoreJS function. Read more about it here.
You loose the context in $.getJSON done callback. Try this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
var _this = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
_this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
You don't have access to this inside " $.getJSON( " assign this to any variable and then call "renderProgress" method.
var currentObj = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
currentObj .renderProgress('4'); //<== pass here
});
because in your case this points to current object of that function and not to view object.

Passing variable into object method javascript

trying to get my head around objects, methods, closures, etc... in Javascript.
Can't see why this isn't working, some fundamental flaw in my thinking I guess. I'm expecting the val variable to be passed through to the addNote() function but it isn't. I thought that any variables declared outside of a function are available to that function, as long as they're not within another function. Is that not correct?
if(typeof(Storage) !== "undefined") {
console.log(localStorage);
var $input = $('#input'),
$submit = $('#submit'),
$list = $('#list'),
val = $input.val();
var noteApp = {
addNote : function(val) {
var item = val.wrap('<li />');
item.appendTo($list);
clearField();
},
clearField : function() {
$input.val = '';
},
delNote : function(note) {
}
};
$submit.on('click', function(){
noteApp.addNote();
});
} else {
}
I'm trying to learn how the pros manage to get their code so clean, concise and modular. I figured a note app would be a perfect start, shame I got stuck at the first hurdle...
Cheers.
There are several issues with the code in the question
defining an argument named val and not passing an argument to the function
when calling clearField() inside the object literal it's this.clearField()
You're only getting the value once, not on every click
val is a string, it has no wrap method
$input.val = ''; is not valid jQuery
I would clean it up like this
var noteApp = {
init: function() {
if (this.hasStorage) {
this.elements().events();
}
},
elements: function() {
this.input = $('#input');
this.submit = $('#submit');
this.list = $('#list');
return this;
},
events: function() {
var self = this;
this.submit.on('click', function(){
self.addNote();
});
},
hasStorage: (function() {
return typeof(Storage) !== "undefined";
})(),
addNote: function() {
this.list.append('<li>' + this.input.val() + '</li>');
this.clearField();
return this;
},
clearField: function() {
this.input.val('');
},
delNote : function(note) {
}
}
FIDDLE
Remember to call the init method
$(function() { noteApp.init(); });
In your call to addNote(), you don't pass any argument for the val, so it will be undefined:
noteApp.addNote();
// ^^ nothing
Pass the input (seems you want the jQuery object not the string value because of your val.wrap call):
noteApp.addNote($input);
When you declare the val in the function, it is scoped to that function and will only be populated if the function call passes a value for that argument. Even if you have another variable in an upper scope with the same name val, they are still differentiated. Any reference to val in the function will refer to the local val not the upper scope.

knockout pass additional parameters to subscription function

What I want to achieve is to create subscription for model properties. This subscription function should call WebApi via Ajax updating property value in database. For ajax call I need three paramaters: "fieldName", "fieldValue" and "modelId", ajax will update database row based on those three parameters.
I have many properties and all of them need the same functionality, so I do not want to subscribe for each property individually, so I found a following suggestion:
ko.subscribable.fn.withUpdater = function (handler) {
var self = this;
this.subscribe(handler);
//support chaining
return this;
};
Add this is how it is "attached" to observables:
self.ModelId= ko.observable();
self.CompanyName = ko.observable().withUpdater(update);
where update is some js function outside model.
However, I have problem, because I am not able to pass three paramaters to update functions (or also I can say in another words - I need to be able to get viewModel.ModelId property value inside update, as well as propertyName).
function update (propertyName, propertyNewValue, anotherPropertyValue) {
//do ajax update
}
As an example for CompanyName property it will be:
update("CompanyName", "New Company value here", 3),
where
3 == viewModel.ModelId
There might be a better way to do this, but the following will work:
First, add a target object to the withUpdate method:
ko.subscribable.fn.withUpdater = function (handler, target, propname) {
var self = this;
var _oldValue;
this.subscribe(function (oldValue) {
_oldValue = oldValue;
}, null, 'beforeChange');
this.subscribe(function (newValue) {
handler.call(target, _oldValue, newValue, propname);
});
return this;
};
The update subscribe function will get scoped to the target property:
var update = function (propertyName) {
console.log('propname is '+ propname + ' old val: ' + oldvalue + ', new val: ' + newvalue + ', model id: ' + this.ModelId());
}
Now you will need to use it a little differently.
self.CompanyName = ko.observable().withUpdater(update, self, "CompanyName");
An example http://plnkr.co/edit/HhbKEm?p=preview
I couldn't get the scope of the withUpdater function to be that of the object without explicitly passing in the target and a string for the company name.
You can declare your function as a variable outside of the 'fn' scope.
var dataservice = 'my class that has the data calls';
var altFunc = function () {
return ko.pureComputed(function () {
var currentItem = this().filter(function (item) {
// Do knockout stuff here and return your data
// also make calls to the dataservice class
}, this, dataservice);
};
ko.observableArray.fn.someNewFunctionality = altFunc;

Invoke public function from internal/private function?

Just wondering if I'm missing something or not but I attempted to do the following:
(function() {
var thing = function() {
var doIt = function() {
console.log("just do it");
this.updateValue(5);
};
return {
updateValue: function(val) {
console.log('updating value: ' + val);
},
go: function() {
doIt();
}
}
};
var t = thing();
t.go();
}())
This results in "just do it" showing up in the console followed by an error b/c it says "updateValue" is not a function.
I was wondering, can an internal/private function (e.g. "doIt") invoke a public function (e.g. "updateValue")? Perhaps this is just bad design and you should never really want to do this and I've actually refactored my code to avoid/not do this but I was curious if it was possible.
Thanks in advance.
Either use call/apply to explicitly specify the context for this (like #SLaks and #Alnitak) mentioned or else define the function at the beginning and then add it as a property to the returned object:
var thing = function() {
var updateValue = function () { /* */ },
doIt = function() {
console.log("just do it");
updateValue(5);
};
return {
updateValue: updateValue, // minor duplication here
go: function() {
doIt();
}
};
};
If the minor duplication annoys you, you can also do this:
var thing = function() {
var exposed = {
updateValue: function(val) {
console.log('updating value: ' + val);
},
go: function() {
doIt();
}
}, doIt = function() {
console.log("just do it");
exposed.updateValue(5);
};
return exposed;
};
Writing doIt(), calls the function in the global context, so this is the window object.
You need to write doIt.call(this) to pass your this as the context for doIt.
Per #SLaks answer, this is incorrect when invoked by doIt().
Instead, try:
doIt.call(this);

Categories