Spine.js get associated controller from element - dom

I got an html-element aside.sidebar with an associated spine.js controller.
class App.Sidebar extends Spine.Controller
tag: 'aside'
className: 'sidebar'
how do i get the controller from the element? something like this:
con = $("aside.sidebar").spineController().someControllerMethod()
I'm using jquery + spine.js.

I've looked through the controller source code and it doesn't look like there is a built-in way to access the controller from the element (https://github.com/spine/spine/blob/dev/src/spine.coffee#L482). What I do is make a global variable to hold the controller so that I can access it from other places:
app = new App()
# inside of App constructor:
#foo_widget = new App.FooWidgetController()
# from other non-spine code:
app.foo_widget.method()
Another option would be to do the association yourself using jquery's data() method:
class App.FooWidgetController
constructor: ->
super
#el.data 'controller', this
# from other code:
$('#widget').data('controller').method()

Related

How do I override inherited methods when using JavaScript ES6/ES2015 subclassing

I have created a class that extends Array. I want to execute arbitrary code before calling the inherited push function.
class newArray extends Array{
//execute any logic require before pushing value onto array
this.push(value)
}
The solution I found was to create a new function in the subclass that has the same name as the inherited function. In this case push. Then inside the overriding function the inherited function is called via the super keyword.
class newArray extends Array{
push(value) {
//execute any logic require before pushing value onto array
console.log(`pushed ${value} on to array`)
super.push(value)
}
}
var array = new newArray
array.push('new Value')

ReactJS dynamically call custom class

I am trying to setup a way to create form objects dynamically from some json values. Essentially, I have in my json the form object type and properties. I pass that type to a FormInput class that then calls the custom class containing the actual form object. My problem right now is that when I pass in the custom class name "TextInput" (this.props.formElementType) React just creates an element called 'textinput' instead of calling the class. It doesn't appear to like passing in a string, but wants just the classname. Essentially,...
TextInput = React.createClass({...})
...
FormItem = React.createElement(<TextInputPassedAsAString>, {...})
I am not sure if I can call a custom class this way or not, by passing a string. I need help with this implementation or a better idea as I am relatively new to React.
Below is all the relevant code starting with the children ending with the final render block. Please excuse the pseudo coffeescript.
TextInput
TextInput = React.createClass
handleChange: (event)->
this.setState
value: event.target.value
render: ->
React.createElement('label', {
value: this.props.formElementLabel
})
React.createElement('input', {
id: this.props.formElementID,
type: 'text'
})
module.exports = TextInput
FormElement
FormElement = React.createClass
render: ->
R.div(null,
React.createElement(this.props.formElementType, {
formElementID: this.props.formElementID,
formElementLabel: this.props.formElementLabel
})
module.exports = FormElement
The initial call/final render
React.createElement(FormElement, {
formElementType: 'TextInput',
formElementID: 'firstFormElement',
formElementLabel: 'First text input'
})
Well, the best way, easiest to reason about, etc. is probably to require TextInput in the module that's doing your final render. Create it there, passing in the other props to it, and then pass this created component to FormElement rather than passing the string representation of the component.
One way to do this more dynamically would be to have have a module that exports each dynamic component as a property/method on the export. Something like:
module.exports = {
TextInput: ...
}
Then when you're rendering you could pass in something like this:
myImports[json.formElementType]

CoffeeScript mixin doesn't work

I found this example of mixins in coffeescript faq but it seems like it doesn't work.
Am I missing something here?
extend = (obj, mixin) ->
for name, method of mixin
obj[name] = method
include = (klass, mixin) ->
extend klass.prototype, mixin
class Button
onClick: -> alert "click"
class Events
include Button, Events
(new Events).onClick()
# => Uncaught TypeError: Object #<Events> has no method 'onClick'
fiddle
You are missing the fact that onClick is defined on the prototype of Button,
and that you did not set the arguments with the right order in the include function
extend = (obj, mixin) ->
for name, method of mixin
obj[name] = method
include = (klass, mixin) ->
extend klass.prototype, mixin
class Button
onClick: -> alert "click"
class Events
include Events,Button.prototype
(new Events).onClick()
see the "fiddle"
So the mixin snippet works pretty well.

Silverstripe - assign Template to Controller manually

at the moment I´m working with a custom Silverstripe Controller with a Director rule:
---
Name: myroutes
After: framework/routes#coreroutes
---
Director:
rules:
'category/$Action/$Slug': 'Category_Controller'
The Controller looks like that:
class Category_Controller extends Page_Controller {
public function show($arguments) {
echo "Slug: " . $arguments->param("Slug");
}
}
When I open in the browser the URL http://mysite.com/category/show/mobile
then the output look fine like this: "Slug: mobile".
I just wonder how I can use a Category.ss Template from the Folder "themes/templates/Layout" to render the Output. Then of course the container html (with header/footer) from Page.ss should be included as well. Just as usual when you have a custom Page Controller/Class and a corresponding Template in the Layout Folder.
I just tried this:
public function show($arguments) {
echo $this->renderWith("Category");
}
It uses Category.ss for rendering the output, but there is no container html...
Thx for any help.
Regards,
Florian
you can also pass an array to renderWith(), and it will try through the array until it finds a template.
so lets say $this->renderWith(array('Category', 'Page'));
it will first look for a template called Category.ss, but will not find it (in the templates folder, not layout folder), it will then find Page.ss and use it.
Now it hits $Layout inside Page.ss and it checks the array again inside the Layout folder, it will now find the Category.ss, which is exactly what you where looking for if I got the question right.
if you do not want to do return $this->renderWith(); you can also just do return $this; and silverstripe will get the action you called and the class hierarchy of $this and use that as array for renderWith()
So if your classes are Category_Controller > Page_Controller > ContentController the array will look like this:
array(
'Category_show', // because your action is show
'Category',
'Page_show',
'Page',
'ContentController_show',
'ContentController',
)
(I am not a 100% sure if it also includes Page_show and ContentController_show.)

How to run same lines in all controllers init() function?

I need same 2 lines in all my controllers, each controller have its own init logic, but these two lines are common for all of them.
public function init()
{
$fm =$this->_helper->getHelper('FlashMessenger');
$this->view->messages = $fm->getMessages();
}
How can I avoid repeat code ?
Update:
Ok, the FlashMessenger was only an example, let's say I need write a log line in every action except for 'someAction' # 'someController'. So the new common lines should be.
$this->logger = new Zend_Log();
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH.'/../logs/log.txt');
$this->logger->addWriter($writer);
$this->logger->log('Some Message',Zend_Log::DEBUG);
The question is, where should I place these lines in order to avoid repeat them in all init() of each controller.
These lines should be placed at bootstrap?. If so: How can skip log lines for 'someAction'.
Or should I implement a 'BaseController' and make all my controller extend from it. If so: How can I Autoload it? (Fatal error: Class 'BaseController' not found) .
Just subclass the controller:
class Application_ControllerAction extends Zend_Controller_Action {
public function init()
{
$fm =$this->_helper->getHelper('FlashMessenger');
$this->view->messages = $fm->getMessages();
}
}
class IndexController extends Application_ControllerAction {
}
You may also achieve the same writing Controller Plugin.
Edit:
Front controller plugins are executed on each request, just like the Controllers and have the same hook methods:
routeStartup(): prior to routing the request
routeShutdown(): after routing the request
dispatchLoopStartup(): prior to entering the dispatch loop
preDispatch(): prior to dispatching an individual action
postDispatch(): after dispatching an individual action
dispatchLoopShutdown(): after completing the dispatch loop
I addition, you may check controller params to execute the code only on selected requests:
if ('admin' == $this->getRequest()->getModuleName()
&& 'update' == $this->getRequest()->getActionName() ) …
You can access your flash messages through (you dont need to send anything from your controller to your view, it's all automated)
$fm = new Zend_Controller_Action_Helper_FlashMessenger();
Zend_Debug::dump($fm->getMessages());
in you view, i would also recommand that you encapsulate this code in a view helper like it is shown on this site http://grummfy.be/blog/191
In your bootstrap:
protected function _initMyActionHelpers() {
$fm = new My_Controller_Action_Helper_FlashMessenger();
Zend_Controller_Action_HelperBroker::addHelper($fm);;
}
How can I avoid repeat code ?
Write your own custom controller, implement that in init method of that controller, and then extend all controllers in your app from your custom controller.
But approach with separate view helper as #Jeff mentioned (look at link) is often taken as a better solution.
In your controller do only:
$this->_helper->flashMessanger('My message');
and view helper will do the rest.
It's not the reason for creating new custom controller. Just add this line to all you init() methods.
$this->view->messages = $this->_helper->getHelper('FlashMessenger')->getMessages();