I'm trying to pass a reference to a parent class instance to its child class. The parent class is:
class UI
constructor: (parameters) ->
#addBTS = new AddBTS
#toolbar = new Toolbar(this)
The AddBts and Toolbar classes are:
class Toolbar
constructor: (#parent) ->
add_bts_clickhandler: () =>
$('body').on 'click', '#add_bts', ->
#parent.addBTS.display()
class AddBTS
constructor: () ->
display: () =>
$("#setBuildingTileSet").show()
console.log 'Showing Sir'
All these classes are defined in separate CoffeeScript files and are joined together before comalation in the following order (Game is where the instance of UI lives):
'src/Game'
'src/UI/UI'
'src/UI/AddBTS'
'src/UI/Toolbar'
No, when I call add_bts_clickhandler: () I get the error message: * Cannot read property 'addBTS' of undefined *
So it thinks that parent is undefined, when it's clearly not.
Please could someone help me, I'm completely stuck as my code seams fine.
My solution:
I call add_bts_clickhandler:
ui = new window.UI();
ui.toolbar.add_bts_clickhandler();
My classes:
class window.UI
constructor: (parameters) ->
#addBTS = new window.AddBTS
#toolbar = new window.Toolbar(this)
class window.AddBTS
constructor: () ->
display: () =>
$("#setBuildingTileSet").show()
console.log 'Showing Sir'
class window.Toolbar
constructor: (#parent) ->
add_bts_clickhandler: () =>
p = #parent ## introduce a new variable!
$('body').on 'click', '#add_bts', ->
p.addBTS.display(); ##
##parent.addBTS.display()
Let us look Toolbar class a bit closer:
class window.Toolbar
constructor: (#parent) ->
add_bts_clickhandler: () =>
$('body').on 'click', '#add_bts', ->
#parent.addBTS.display()
Part of the generated javascript:
Toolbar.prototype.add_bts_clickhandler = function() {
return $('body').on('click', '#add_bts', function() {
return this.parent.addBTS.display();
});
};
In this line: return this.parent.addBTS.display();
this refers to the callback function()! not to the add_bts_clickhandler.
I hope I helped you.
Related
ReactNative:
I use react-navigation Component.
In the navigation bar on the right button, click the event is not Working
`_newCustomer() {
alert('点击新建');
}
static navigationOptions = {
title: '客户列表',
headerRight: (
<Button title={'添加客户'} onPress={this._newCustomer.bind(this)}/>
),
}
The Error:
undefined is not an object(evaluating 'this._newCustomer')
The problem is because you're trying to call this._newCustomer from a static function, and this is undefined, because you're in a static function.
If you look at the React Navigation Docs you'll see that in the examples they use anonymous functions. Do that, or call another static function instead.
static navigationOptions does not have capability to link your dynamic 'this' variable. Normally you need to create a custom Button component and use this component to deal with click event.
static navigationOptions = ({ navigation }) => {
return {
headerRight: (
<EventButton
navigation={navigation}
/>
),
};
};
and then
export const EventButton = (props) => {
let testButton = <TouchableHighlight onPress={() => props.navigation.navigate('CreateNewCustomer',{ name: 'CreateNewCustomer'})}>
</TouchableHighlight>
return testButton
}
I'm using ReactJS with CoffeeScript (:
Actually I have a component A which handle a state. A state field is passed to the child (called myChild in that example). The child needs to update some value from the parent state.
How can I do that in the ReactJS way ?
A = React.createClass
getInitialState:
mystate: {test: 1}
render: ->
myChild {param: #state.mystate}
myChild = React.createClass
render: ->
React.DOM.div {onClick: #change}, #props.param.test
change: ->
#props.param.test += 1 # WRONG WRONG WRONG
ajax("/..../", JSON.stringify(#props.param))
.done(#onDone).fail(...)
onDone: ->
console.log "Hum..."
Comments:
-#props.param.test can't be changed like that (for coherence and it's should be immutable).
-#props.param is in fact the object inside the state of component A, thus it should be updated with #setState which is not possible since we are in the child :(
How can I clear this to have a good ReactJS component ?
Paul ? Will you still help me ? :D
The parent is the source of truth, so the child needs to tell the parent to change its state. Pass a callback from the parent to the child, and have the child call it.
There's an example on communicating between components in React's docs that might be helpful too.
A = React.createClass
getInitialState: ->
mystate: {test: 1}
incrementTest: ->
#setState {mystate: {test: #state.mystate.test + 1}}
render: ->
myChild {onChange: #incrementTest, param: #state.mystate}
myChild = React.createClass
render: ->
React.DOM.div {onClick: #change}, #props.param.test
change: ->
#props.onChange
ajax("/..../", JSON.stringify(#props.param))
.done(#onDone).fail(...)
onDone: ->
console.log "Hum..."
I have a coffeescript class that has some jquery event listeners. I would like to use the fat arrow => to avoid having to reference the class, but I still need a reference to the element that would usually be used with this. How can I use both?
class PostForm
constructor: ->
$('ul.tabs li').on 'click', =>
tab = $(this)
#highlight_tab(tab)
#set_post_type(tab.attr('data-id'))
highlight_tab: (tab)->
tab.addClass 'active'
set_post_type: (id) ->
$('#post_type_id').val(id)
CoffeeScript links both this and # to the outer context, therefore you cannot access the context that jQuery provided (aka the desired "this"). Use event.target instead:
class PostForm
constructor: ->
$('ul.tabs li').on 'click', (event) =>
tab = $(event.target)
#highlight_tab(tab)
Using evt.currentTarget
You should probably use evt.currentTarget (which is equivalent to this) instead of evt.target (which isn't). If the node that you are tapping for click notifications has child nodes, evt.target might be one of those child nodes instead of the node you added the click handler to.
See http://codepen.io/ddopson/pen/erLiv for a demo of this behavior. (click on the inner red box to see that currentTarget points at the red div while target points at outer blue div that the event handler is registered on)
$('ul.tabs li').on 'click', (event) =>
tab = $(event.currentTarget)
#highlight_tab(tab)
Answer to the question asked - getting both `=>` and `this`:
You can do the following...
$('ul.tabs li').on 'click', (event) =>
tab = $(` this `) # MAKE SURE TO ADD THE SPACES AROUND `this`
#highlight_tab(tab)
The spaces are critical as they prevent Coffee from munching this into _this.
Using `self` and `->`
Alternatively, do the following ...
self = this
$('ul.tabs li').on 'click', (event) ->
tab = $(this)
self.highlight_tab(tab)
This is similar to CQQL's answer, except that I prefer the idiomatic use of self as the variable name; my VIM syntax highlighting rules color self as a "special" variable just as it would for this, arguments, or prototype.
I prefer this version, because I can understand it more easily.
class PostForm
constructor: ->
post_form = this
$('ul.tabs li').on 'click', (event) ->
tab = $(this)
post_form.highlight_tab(tab)
You may want to access variables set in the constructor from your functions. This would be how you do it (the key is calling the function via self while first extracting this with a thin arrow):
class PostForm
constructor: ->
self = this
#some_contrived_variable = true
$('ul.tabs li').on 'click', ->
tab = $(this)
self.highlight_tab(tab)
self.set_post_type(tab.attr('data-id'))
highlight_tab: (tab) ->
# Because of the fat arrow here you can now access # again
if #some_contrived_variable
tab.addClass 'active'
set_post_type: (id) ->
$('#post_type_id').val(id)
BTW: This is a great explanation of when to use the fat and thin arrow.
Summary:
Do you use this (#) in the function?
Do you want to execute the function later, possibly from a different scope?
I have two very simple Spine.js controllers:
class ListController extends Spine.Controller
className: 'list'
constructor: () ->
super
class DetailController extends Spine.Controller
className: 'detail'
constructor: () ->
super
controller stack
class Application extends Spine.Stack
className: 'mystack'
controllers:
list: ListController
detail: DetailController
and corresponding HTML markup
<div class="mystack">
<div class="list">list</div>
<div class="detail">detail</div>
</div>
My problem is that when controller stack instantiated
app = new Application()
app.list.active()
there is no active class added to the div.list element. Divs remain unchanged.
What is wrong with that?
I've just got it so I'll describe basic working example. There are several issues with the code above (caused by my misunderstanding of how Spine.js controller stack works :-)
First, appropriate HTML element have to be associated with every controller managed by the stack. When controller stack instantiates the controller it passes only stack (i.e. itself) instance as parameter to the constructor. So controller constructor have to take it into account (e.g. like the following):
class ListController extends Spine.Controller
constructor: (parameters) ->
#stack = parameters.stack
#el = $ #stack.settings.listSelector
super
class DetailController extends Spine.Controller
constructor: (parameters) ->
#stack = parameters.stack
#el = $ #stack.settings.detailSelector
super
and the stack:
class Application extends Spine.Stack
settings:
listSelector: '.list'
detailSelector: '.detail'
controllers:
list: ListController
detail: DetailController
default:
'list'
then the controller stack could be instantiated:
app = new Application
el: $ '.mystack'
ListController will be active (i.e. corresponding div has active class added) by default and anytime later you can call #stack.detail.active() or #stack.list.active() from controller instance method to activate required controller and 'hide' (i.e. remove active class) the other(s).
EDIT:
We discussed the issue with #aschmid00. In fact, controller constructor doesn't have to set its own property #stack manually. It is done automatically when base constructor called by super. But in case of this question #el have to be set before base constructor called due to the events delegation etc.
I'm having real problems writing a simple Backbone.js app using CoffeeScript and Zepto.js
This is the simplest Backbone view yet the events don't fire. I get no errors in the console either? Where am I going wrong?
#Main view
class AppView extends Backbone.View
constructor: ->
#el = $("#books")
#template = _.template("<div>New Item <a href='' id='addNew'> add new item</a></div>")
events: {
"click" : "createNew"
}
render: =>
#el.html(#template())
createNew : ->
console.log "new"
#Onload
$(document).ready ->
view = new AppView
view.render()
I've been following the only example I can find of CoffeeScript & Backbone together https://github.com/bnolan/Backbone-Mobile/blob/master/application.coffee
However if I add super into my view code above I get an undefined error, his code does not.
The class Backbone.View has its own constructor that does plenty of work, and you are overriding it and not calling super. Bad.
Instead, Backbone.View provides you the ability to define your own constructor-type function called initialize. Perform all your setup there. Backbone.View#constructor will call initialize.
#Main view
class AppView extends Backbone.View
initialize: ->
#el = $("#books")
#template = _.template(
"<div>New Item <a href='' id='addNew'> add new item</a></div>"
)
I had a similar problem (events not firing) and found that the problem was due to not setting #el. I set that:
#el: $("#content")
and it worked.