I want to manage part of my layout with coffeescript. I have a left/right panel and I want to be able to switch between them. I have created something like this:
window.switchPanel = (panel = 'left', action = 'toggle') ->
open = (panel) ->
...
close = (panel) ->
...
toggle = (panel) ->
...
My question is, how do I structure this so that I can call open/close/toggle by the action variable and can I use something so that I don't have to pass in the panel to every child function? Perhaps #panel?
I think you just want to throw your functions into an object so that you can access them by name:
window.switchPanel = (panel = 'left', action = 'toggle') ->
funcs =
open: (panel) ->
...
close: (panel) ->
...
toggle: (panel) ->
...
Then you can simply funcs[action](panel) inside switchPanel. If you don't want pass panel into the functions then you don't have to, they'll have access to panel simply by being defined within switchPanel:
window.switchPanel = (panel = 'left', action = 'toggle') ->
funcs =
open: ->
...
close: ->
...
toggle: ->
...
Then you'd just funcs[action]() and they could do what they like with panel.
Demo: http://jsfiddle.net/ambiguous/UV42x/
Some reading on JavaScript closures would clarify what's going on in the second version.
You might want to include an if(action !of funcs) check to make sure you don't try to use a bad action. Or, as Aaron Dufour notes in the comments, you could funcs[action]?() if you only need to check that action is valid once.
Few things:
1) I think you're creating obvuscated architecture by selectively calling methods within the object based on an input to the object. It begs the question "Why even create the methods? Why not just use a big IF ELSE IF type structure?" But others would disagree with me.
2) This is one of the confusing circumstances of CoffeeScript. You're dealing with something that would normally be clear if wrapped in explicit braces. I don't like javascript's crazy use of anonymous function wrappers everywhere, but I do believe in bracketed code. I defer to point 1. I think you're just making things more complicated by doing it this way.
3) Have you studied how coffeescript transforms the 'this' keyword? I'm pretty certain CoffeeScript does some kind of smart transformation on 'this' that deviates from standard javascript, which will mean nobody can answer this question for you except Coffee users. It won't be the same as javascript. Its relevant because in order to access 'panel' in the methods without passing it explicitly, you would need to use 'this.panel' or some other dereferencing mechanism to access the panel member from within one of the methods. I can't tell you how, but this information my help steer you in the right direction. The issue at hand is clearly a question of how to reference object members.
Related
Good day,
I have several partials that have code. In the code tab, I noticed that the code tab had similar looking code. Here are examples
Partial 1
function onStart()
{
$x = MyModel1::where('myColumn', 'myValue')->first();
// lots of stuff using $x functions
$this['viewData'] = $x->getViewData();
}
Partial 2
function onStart()
{
$x = MyModel2::where('myColumn', 'myValue')->first();
// lots of stuff using $x functions
$this['viewData'] = $x->getViewData();
}
MyModel1 and MyModel2 both implement the same interface, so they have the same functions.
My question is, where do I put the code that is similar? I can put it in a plugin but that doesn't feel correct. I can create a base class and have the partials call the parent method but won't that mean modifying the code in the vendor folder?
if you really need to manage your code you can create component and add that code there as they easily attached to other pages (down point is that you need to create a plugin)
you can write your code inside onRun method.
https://octobercms.com/docs/plugin/components#page-cycle
and instead directly assigning variables to this you need to assign them like
$this->page['var'] = 'value';
and now it will work same as you are doing.
I started using CoffeeScript today and found myself using a pattern like (args...) => #style(args...) a lot when I needed callback function. The context looks roughly like this:
class Parent
#style: (feature) ->
if feature
#insight()
class Child extends Parent
#insight: ->
alert 'Sara is awesome'
#load: ->
[42].forEach((args...) => #style(args...))
Child.load()
This shows Sara is awesome, which is accurate. If I had only used [42].forEach(#style), style would’ve ended up with a this referring to the parent class (I think?), which doesn’t know insight.
But this is very verbose, and I need a lot of callback functions in my code. Is there a more elegant, idiomatic way to solve this?
(Using forEach in CoffeeScript is bad style I’ve read, but in my actual code I’m working with various Leaflet functions that I can’t just replace with for loops.)
First thing to noticed is that you shouldn't call insight from the Parent class. The whole purpose of a class is to provide encapsulation. So the first thing I'd do is to move insight to Parent
To answer your question, the more idiomatic way to solve that is using the fat arrow notation. What the fat arrow does internally is to create an anonymous function to enclosure the this.
That said, the final code should look something like this:
class Parent
#style: (feature) =>
if feature
#insight()
#insight: ->
alert 'Sara is awesome'
class Child extends Parent
#load: ->
[42].forEach(#style)
Child.load()
Hope that helps.
EDIT
Based on the OP comment:
class Parent
style: (feature) =>
if feature
#insight()
class Child extends Parent
load: ->
[42].forEach(#style)
insight: ->
alert 'Sara is awesome'
(new Child()).load()
Perhaps I'm using the wrong search terms, or maybe eclipse just doesn't support this, but when I type a function call to a function that I haven't written yet, is there a way to have eclipse automatically create an empty placeholder function with the same name?
I'm using FDT to do ActionScript3 coding, if that makes any difference.
For example, if I type this:
var x = func(5.2);
but I haven't written the func function yet, eclipse will underline func to alert me that it can find a reference to that function. This is presenting me with a problem, but not a solution. Is there a keyboard shortcut to have eclipse automatically go to this:
var x = func(5.2);
private function func():void {
// add your code here...
}
Use Ctrl-1 (Windows) or Cmd-1 (MacOS).
Having a bit of hardtime understanding coffeescript. Why is this the window object in the set_position function?
window.App = {}
$ ->
driver = new Driver if ($('#drivers_become').length >= 1)
window.App.driver = driver
class Driver
constructor: ->
#get_position()
get_position: ->
if navigator.geolocation
navigator.geolocation.getCurrentPosition(#set_position)
set_position: (pos) ->
# this refers to window object in this case. why?
#latitude = pos.coords.latitude
#longitude = pos.coords.longitude
get_latitude: ->
#latitude
get_longitude: ->
#longitude
get_latitude and get_longitude return undefined in this case.
If you are using aDriverInstance.set_position as an event handler function, the browser will invoke it as a regular function not a method. To fix that, use a "fat arrow" when defining it: set_position: (pos) =>. But more broadly it is a question of invoking via dot notation and invoking via direct reference:
aDriverInstance.set_position(pos) will have this set to aDriverInstance and all is well
set_position_reference = aDriverInstance.set_position;set_position_reference(pos) will have this set to the window object.
This is a classic binding issue, and applies as much to Javascript as Coffeescript.
You are passing a Driver method, set_position to a Windows function,
navigator.geolocation.getCurrentPosition(#set_position)
That function evaluates set_position in the global, windows, context. In effect it ends up setting global latitude and longitude variables, not the attributes of the Driver instance. In your console see if those variables are defined.
What you want to do is define set_position so #is bound to the Driver instance. To do that, use the fat arrow, =>. http://coffeescript.org/#fat-arrow
set_position: (pos) =>
# this refers to window object in this case. why?
#latitude = pos.coords.latitude
#longitude = pos.coords.longitude
If you use this, and look at the compiled Coffee, you will see a line that does:
this.set_position = __bind(this.set_position, this);
Packages like jquery and underscore also have a bind function, as do recent browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
get_position: ->
if navigator.geolocation
navigator.geolocation.getCurrentPosition(#set_position.bind(this))
Uses the browser's bind
Hi I have a situation where I need to look up the number of recently viewed products on catalog/product/view.phtml. In the recently viewed 'product_viewed.phtml' file it calls
$_products = $this->getRecentlyViewedProducts()
to get the recently viewed. How would I access this method from within the catalog/product/view.phtml file?
I don't know where this method is. I've tried searching for it but it doesn't seem to exist. When I write click it in Netbeans and click go to declaration it takes me to
class Mage_Reports_Block_Product_Viewed extends Mage_Reports_Block_Product_Abstract
Actually on the class itself. This class only has _toHtml(), getCount(), and getPageSize() methods.
I just need to know whether there are any recently viewed products.
Any help most appreciated!
Billy
If you look into 'Mage_Reports_Block_Product_Viewed', you will notice:
$this->setRecentlyViewedProducts($this->getItemsCollection());
That 'getItemsCollection' method is defined in the abstract class... And you will notice this abstract class will create a model based on $_indexName defined in the (subclassed) block.
If you just want the collection, you can probably get away with:
$_products = Mage::getModel('reports/product_index_viewed')->getCollection();
And then adding whatever you want to the collection:
$_products
->addAttributeToSelect('*')
->setAddedAtOrder();
// optionally add other methods similar to Mage_Reports_Block_Product_Abstract::getItemsCollection
Another approach that might be more suited would be to create the original block:
$productViewedBlock = $this->getLayout()->createBlock('reports/product_viewed');
On which you can simply call whatever you want:
$_collection = $productViewedBlock->getItemsCollection();
$_count = $productViewedBlock->getCount();
The getRecentlyViewedProducts function is a magical getter that gets the data that was set with setRecentlyViewedProducts in app/code/core/Mage/Reports/Block/Product/Viewed.php (which builds it using app/code/core/Mage/Reports/Block/Product/Abstract.php's function _getRecentProductsCollection).
This is complicated stuff that you don't want to reproduce; its better, IMO to make your own Block that extends Mage_Catalog_Block_Product_Abstract that will give you access to the same functionality, and drop your new block into the page you're working on.