I have a basic Spine/coffeescript app and I am trying to get the parameters out of the route that I have set up, by looking at examples of other applications. Here is the basic stack:
class Main extends Spine.Stack
el: "#main"
controllers:
sources: Sources
default: 'sources'
routes:
'/sources/:id': 'sources'
Here is the sources controller:
class Sources extends Spine.Controller
el: '#something'
constructor: ->
super
active: (params) ->
super
#id = params.id
#refresh()
#render()
However, params is undefined when active is called. I am navigating to /#/sources/soemthing. Maybe there is something about the sugar in the stack that I don't understand, but the example I've seen seems to just work in this way. Can anyone tell me what is missing?
So apparently the stack always initializes the default controller with no parameters before switching to one of the other controllers, even if you navigate to /#/route/parameter directly. After adding a default controller that does nothing, this problem went away.
class Main extends Spine.Stack
el: "#main"
controllers:
home: Home
sources: Sources
default: 'home'
routes:
'/': 'home'
'/sources/:zooniverse_id': 'sources'
P.S. If you are trying to get Spine help on SO, don't. Go to the SpineJS google group: https://groups.google.com/forum/#!forum/spinejs
Related
A bit simplified, my app routes have three levels of nesting:
/profile
/course
/quiz
These are represented by named routes, /profile, /profile/course and /profile/course/quiz. These routes map to widgets: ProfileScreen, CourseScreen and QuizScreen. Each of these takes an argument: the Profile, Course and Quiz model objects, respectively.
If I understand correctly, the Navigator will ensure that parent routes will always be on the stack before child routes, so when we're on the /profile/course/quiz route, we'll have /profile/course above that.
Now I'm inside the QuizScreen widget, and I want to access the current Course. How do I do that?
It's somewhere on the route stack, but ModalRoute.of(context) will of course give me the /profile/course/quiz route, not the /profile/course route, so ModalRoute.of(context).settings.arguments will be a Quiz.
I tried wrapping the CourseScreen in a Provider<Course> widget (from the excellent providers package) which provides the Course down to child widgets, but sub-routes actually become sibling widgets, not children.
Of course I could explicitly pass the Course down to the /profile/course/quiz route as a second argument (wrapped together with the Quiz in an array or object), but this seems needlessly convoluted and doesn't scale well.
Is there a way to get the route arguments of a parent route? Or does this actually indicate that I'm doing it wrong and should be designing my app differently?
Of course I could explicitly pass the Course down to the /profile/course/quiz route as a second argument (wrapped together with the Quiz in an array or object), but this seems needlessly convoluted and doesn't scale well.
I found a compelling argument for this approach after all: testability. It means that the QuizScreen can be tested independently of all the other screens and even the routing system.
So I created a simple class to hold the arguments:
class QuizScreenArgs {
final Course course;
final Quiz quiz;
QuizScreenArgs(this.course, this.quiz);
}
Then I realized I can take this one step further and make each route a class:
class QuizRoute extends MaterialRoute<QuizResult> {
QuizRoute(Course course, Quiz quiz) : super
}
This has the advantage that both the route arguments and the route's return type are explicitly typed. No more ModalRoute.of(context).settings.arguments as IHopeThisIsWhatWasPassed or (await Navigator.of(...).pushNamed(...)) as IHopeThisIsWhatWasReturned.
It needs to be a class (rather than just a factory method) so we can use Navigator.popUntil:
Navigator.of(context).popUntil((route) => route is CourseRoute);
All in all, I'm quite happy with this approach and I'm surprised that the docs don't recommend or even hint at this possibility.
I am following up the Heros tutorial from angular 2.0 section.5 services.
The documentation says its not necessary to wrap the this.heroes inside a function
constructor(private heroService: HeroService) { }
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
which doesnt work. and getting error as
Error: TypeError: Cannot read property 'getHeroes' of undefined(…)
Whereas if i use onInit then it works (the completion of the section).
constructor(private heroService: HeroService) {
}
ngOnInit() {
this.getHeroes();
}
getHeroes() {
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}
Why i am getting the result only while running under ngOnInit?
My understanding is that the component class is a kind of toolbox, where you define properties and methods.
Methods will then respond to Angular hooks - like ngAfterViewInit or ngOnInit - or to events, like click().
Sometimes components need to load data when they are instanciated, in which case you can use ngOnInit or constructor to call your loading method.
You do not directly call methods in your class definition. I think that if you did, it could be an issue when importing a class, or when extending a class.
You don't want the class to go fetch your data each time you import your class; rather, you want to have complete control over when your data will be imported.
Here are the best practices for Angular 2
Imagine that coffeescript class :
class Batman
constructor: ->
alert "Batman is awesome"
I think it's a rookie question, but what's the real difference between :
class #Batman
constructor: ->
alert "Batman is awesome"
and
class window.Batman
constructor: ->
alert "Batman is awesome"
Compile your coffeescript using the '-c' argument and see what you get:
(function() {
/// Your code here
}).call(this);
That this there is the context of the function wrapper being called, and becomes the this object inside your coffeescript module.
In the context of a browser, your module is initialized with the global this = window; in the context of a Node or IoJS, this = global, the global context of execution; in the context of plv8, this = role, a per-execute object that contains security information (since plv8 is basically node run inside a SQL server, this is important to have).
class window.Batman explicitly attaches your Batman class to a window object (which means you no longer have isomorphic code you can use everywhere); class #Batman attaches it to the local context, which can be, well, whatever you want it to be.
All in all, as a best practice, attaching stuff to VM-supplied contexts (like the browser, your node instance, your database) is generally not a good idea, and you should find a better way to instantiate your code and pass it from module to module.
I want to have something like
Routes
GET /endpoint pathToTemplate.templateName.scala.html
In order to avoid the need to create a controller just to serve this template.
I need a template because I am serving up values using an imported scala library so this can't just be static html
#import tool.values._
<p>
#Tool.getValue()
<p>
It is impossible to achieve your desire solution but you can to make a workaround with one controller and the dynamic URL parts mapping.
Firstly create a controller which serves a view for any provided path. Route definitions must be placed in code for example with usage of a simple hash map instead of the route file.
object GlobalController extends Controller {
private val getRouterMap = Map(
"view1" -> views.html.view1(),
"view2" -> views.html.view2(),
"sub/view3" -> views.html.view3()
)
def route(path: String) = Action { implicit request =>
Ok(getRouterMap.getOrElse(path, views.html.notFound()))
}
}
Secondly at the end of the route file define a mapping for the created action as follow.
GET /*path controllers.GlobalController.route(path)
It is very important to put it as the last line. Otherwise it will shadow all other mappings defined below.
Hint
Anyway if I ware you I would reconsider your design. Singleton objects aren't easily testable. Sooner or later they will make your life really painful.
As mentioned in the Zend Framework manual, I created a base controller.
Subclassing the Action Controller
By design, Zend_Controller_Action must
be subclassed in order to create an
action controller. At the minimum, you
will need to define action methods
that the controller may call.
Besides creating useful functionality
for your web applications, you may
also find that you're repeating much
of the same setup or utility methods
in your various controllers; if so,
creating a common base controller
class that extends
Zend_Controller_Action could solve
such redundancy.
But it turns out, that Exceptions are not being called properly from the base... To replicate this, create a file:
/path/to/workspace/library/Joe/Controller.php
Then:
class Joe_Controller extends Zend_Controller_Action
{
public function init()
{
Throw new Zend_Exception('test', 500);
parent::init();
}
}
Then in your controller directory, IndexController.php extends off the base:
class IndexController extends Joe_Controller
{
You will find that the exception is uncaught.
If however, you don't extend off the base controller, then throw an exception in init or preDispatch, it will be caught and forwarded to the ErrorController.
Anyone have an idea on getting the exceptions caught from the Base Controller?
Thanks.
UPDATING TO TACKLE THIS ANOTHER WAY
After looking at the various articles and official documentation on how to structure the directories, I went with putting the base controller in library/Joe/...but maybe that's part of the problem...there's no guidance in the manual on how to name and place the base controller. What do the experts with a base controller do?
Another Update
Looking at my code some more, and reading on the net, seems like people suggested in should be:
abstract class Joe_Controller_Action extends Zend_Controller_Action.
Except changing it did not solve the problem...
Now considering that there are articles suggesting to use Base Controllers including the manual, would this be considered a bug in Zend Framework?
To replicate, just throw a Zend_Exception of any kind in the base init or preDispatch. Imagine you poll the database in there, (which is what I'm doing)...and the database is down. No error controller. That's not a good situation unless I'm doing something incorrectly.
My suspicion is that this is a new bug... I don't recall this problem before Zend_Application and I've been using a base controller since ZF 1.5.
Two thoughts off the top of my head:
Controller names in the default module are usually named like SomethingController. Your name Joe_Controller suggests the module Joe and an empty controller name.
In routing/dispatch, doesn't it look for a matching action before dispatch to the controller? Since the samples have no actions, might you be triggering ControllerNotFound exception?
Just throwin' out some ideas.
in Zend philosophy, they provide the init() method to avoid the hassle of
public class Module_TotoController extends Zend_Action_Controller {
public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
parent::__construct($request, $response, invokeArgs);
// some init code here
}
}
just use :
public class Module_TotoController extends Zend_Action_Controller {
public function init ()
// some init code here
}
}
Now, If you look at the Zend_Controller_Action class source, you will notice that the following methods are empty:
init()
preDispatch ()
postDispatch ()
This means, it is useless to call parent:: in your specific controller
public class Module_TotoController extends Zend_Action_Controller {
public function init ()
parent::init(); // useless ;-) but you can go for it
// some init code here
}
}
Now, if you want to put an intermediate class between Module_TotoController and Zend_Action_Controller, you expose yourself to a big hassle as:
Some controllers won't extend your "base" controller (e.g your ErrorController in your question)
so it is not a really a base of your application, is it ?
If you put some logic in your init() of that "base",
you must call the parent:: in your init(),
all the developers in the project need to be aware of that
You will ever add another little features to your "Base" controller
This will result to a big bloated file
Loading/Initializing plenty of stuff you might not really need in that lambda controller
Do you need you database on every page ?
No: doesn't a $this->_helper->connect(); look nice instead ?
Yes: use a controller plugin
This "Base" controller won't fit your other projects needs, so that class won't be reusable
Action helpers will
Controller Plugins will
Hope it makes sense
Don't go for a base or whatever controller
As documentation says :
By design, Zend_Controller_Action must be subclassed in order to create an action controller.
Agreed, this is misleading
What they meant is
class Module_TotoController extends Zend_Controller_Action {}
class Module_TotoController extends Joe_Controller is plain wrong (sorry no offence)
as you said your ErrorController can't extend your intermediate Joe_Controller class
because your exception will be thrown again in the instantiation of the ErrorController class (as excepted !!!)
The uncaught Exception error is the result of a protection to avoid loops (dig Zend\Controller\Plugin\ErrorHanlder.php line 200-ish)
If you require something to be done for every action, use a Zend_Controller_Plugin and register it with the frontController