I'm currently working on a map application with EmberJS, using ember-leaflet for displaying the map. See this jsFiddle for my current setup:
http://jsfiddle.net/HUnnr/1/
My problem is, that I didn't get the click event delegated to the PlacesNewController, only if the /places/new route is open.
my first attempt
Delegating the click event from MapView -> PlacesController -> PlacesNewController, but that did also handle the click event and add a marker to the map, after I changed the route, because PlacesNewController was still alive.
my second attempt
Then I tried Embers Evented mixin. After I had delegated the event from MapView -> PlacesController, I triggered an event. I bind PlacesNewController to this event on PlacesNewRoute.activate and unbind it on PlacesNewRoute.deactivate. The main problem was, that I couldn't access the model of PlacesNewController from the event handling function.
Both solution above doesn't seem to be the "ember way". I think there is a better solution for something like this. Maybe someone could help me to solve this problem. I think, something like direct delegating map events to PlacesNewController would be the cleanest solution, but I didn't know how I could achieve that with Ember.
EDIT:
Here is a new jsBin:
http://emberjs.jsbin.com/uHOPOfi/23/edit
The main problem is, if you first visit /places route, as expected the observers for zoom and isCloseEnough are not called. After that you go to /places/new and the observers are working as expected. But what I didn't understand is, why after going back to /places, the observers and with them also the PlacesNewController are still active? I thought Ember
send an action to the controller.
You can bounce actions around through controllers.
(sorry, switched to jsbin, jsfiddle doesn't fit well on my screen)
http://emberjs.jsbin.com/AguxORAD/3/edit
Personally I'd ditch the new route/controller, it seems weird since all of the real logic lives in places and you'll have to proxy info back and forth, but that's up to you.
I have found a good solution for this problem with the hints from kingpin's answer and this video. The main point is, that every event from the view hits the current route. So the map always delegates the click event. But then it could be processed route depended in PlacesRoute or PlacesNewRoute. So the PlacesNewController does all the place creation logic, the PlacesController does index logic and so on. I think that's a good starting point at the moment.
Here a jsFiddle, that shows my current solution described above.
#kingpin: thank you for your help
Related
I'm using ag-grid and I'd like to save the layout/state of the user. Pretty much something like this
This solution forces the user to click on the button to save the preferences ("Save state"). There is some other way/event to detect that the user changed the state of the table (in order, to me to save and avoid to force the user to click on a button for that)?
I was hopping to find some method here but i didn't..
I initially had code that listened to all of the applicable events from the grid, but ultimately, I found it easier to just save the entire grid state in the component's onDestroy method, regardless of whether anything has actually changed.
Found my answer here.
All the events are here but i prefer to add a global event:
addGlobalListener(listener) Add an event listener for all event types coming from the grid.)
Source: AgGrid javascript grid api
How can we normally prevent Nothing handled the action error in generic view implementation.
Currently I am reopening controller class and adding empty handler but again if I put it directly Ember throws deprecation message Action handlers implemented directly on controllers are deprecated if I add it in action object it is "not working" (probably overridden) and throws error as if it is not in base controller. Any ideas? Thanks.
If you want a somewhat hacky way to do it, you can add the method to the _actions object on the controller. That's where Ember internally keeps all of the actions for an object. Unfortunately, there's no other way to really handle an unused action from a view. This issue suggested a feature that would allow you to, but it hasn't been implemented yet.
Personally, I don't use straight views at all, I only use components. Components allow you to subscribe to particular events (let them bubble up) and ignore the others completely.
All,
I have searched around for an answer to this but have yet to find a good explanation and solution. Hopefully, you can help.
I have a route that renders an Ember.ContainerView (as opposed to a standard Ember.View). When I transition away from this route and then back to it (either via the back button, a link-helper or just changing the url) I get the following error in the console:
Uncaught Error: Assertion Failed: calling set on destroyed object
I understand that a high level reason for this is that when we transition away from the route with the ContainerView, it's childViews are marked as destroyed. When we transition back to the route, the problem has something to do with Ember trying to set the childViews back to the destroyed instances.
I'm not exactly clear but that is the vague understanding I currently have.
So, my questions are as follows:
Can you please correct my understanding of this problem if it is not correct?
Can you please tell me how to fix this error?
For your convenience, I have created a JSBin example for your viewing pleasure. To replicate the error, open your JS console, click on the Go to Route two link, then click on the Go to index link. Now check your logs.
Thanks in advance for any assistance you can provide.
Here is the fix: http://jsbin.com/jumoy/1/edit?html,css,js,output
I have encountered this problem before. I can't say I truly understand it at a very detailed level, but here is the general problem. When you are defining your container view, it is actually creating that subview when you define it, not when you create an instance of the view. This means that your childview 'initial' is only being created one time, and thats when the file is initialled parsed and the ContainerView is defined. When ember cleans this childview up, it is not gone for ever and 'initial' will always be equal to a cleaned up view.
By setting 'initial' as a property, it will be created every time a new instance of your container view is rendered, and therefore a new childview will be created and accessible.
So moral of the story, try not to create objects in definitions of controllers/views. Instead create properties that return the creation of that object.
Hope this helps and makes sense.
UPDATE::
I have edited the jsbin to show you on a deeper level what is happening and why it isn't working. To see the effect uncomment the second ContainerView and comment out the first, you will notice in the console logs that the second time you go to the twoRoute, it has marked that initial property as state: destroying for the view.
http://jsbin.com/jumoy/2/edit?html,js,output
if you are using Ember.Instrumentation namespace for publishing your events and Ember.subscribe to listen to them in your code, when you publish events, it stack all the subscribers and call them even when they are destroyed.
so basically, when for example your route is destroyed which is running subscribe statements it will fail and give above error.
Here is a solution to this problem: Use Ember.Instrumentation.reset() to remove previous subscribers which are destroyed. This solution helped me in acceptance tests where app was destroyed after tests are finished.
I wrote Ember.Instrumentation.reset() in startApp function of module-for-acceptance.js and it worked.
Hope that helps.
Quick context:
Application view has 2 outlets. One for a toolbar. The other for the routable "main" view hierarchy.
app -- main
\-- toolbar
I need some buttons in the toolbar to trigger events in the "main" view. Not update any data in any model. I simply instruct it to trigger some changes to a drawing library that the view is presenting. Clearing the canvas, resetting zoom value and such.
In 1.0 pre2 and earlier I have used actions and router.get('someController.view') to get access to the view I want and trigger the action/method/event. Hardly the pinnacle of application design but it worked fine.
This option is now gone and I am at a loss for a good alternative. What mechanism should I use when communicating between views that are not in a child/parent hierarchy? Everything I have come up with is clunky and triggers my sense that "Ember has a better way".
In short I want:
A toolbar-button to trigger an event
The main view to react to this and perform some updates on parts of itself.
The main view to NOT re-render in the Ember sense of the word as would through routing. It uses a drawing library and integrating all it's properties and behavior into Ember models and controllers would not be a lot of fun.
The toolbar and the main view share a parent view but are on different "branches".
Poor Options I am considering:
The toolbar is very much an application level concern but it does have some buttons that need to instruct specific views. One option I see within Ember is to nest the toolbar under the "main" view. This seems wrong for some of it's other functions.
Communication could be handled by a controller (and possibly even a model) that would hold properties that the toolbar sets and the "listening" view reacts to and resets the value of. This sounds like abuse of the controller and model purposes and like a pretty poor event listener setup.
I could make the drawing library an application global as App.Drawing or something but that does also seems bad. It would also mean that the actions still would not be able to make use of any data in the view to update the drawing library with.
Any suggestions?
What mechanism should I use when communicating between views that are not in a child/parent hierarchy?
In a typical ember application this communication should happen between controllers. Otherwise "Poor Option 2" is on the right track:
Communication could be handled by a controller (and possibly even a model) that would hold properties that the toolbar sets and the "listening" view reacts to and resets the value of.
Consider using two controllers. Toolbar actions will target the ToolbarController, which is responsible for maintaining the toolbar's state and for changing main in response to user action. ToolbarController should declare a dependency on MainController via the needs property. For example:
App.ToolbarController = Ember.Controller.extend({
needs: ['main'],
buttonOneGotAllPressed: function() {
main = this.get('controllers.main');
main.turnOffAnOption();
main.makeSomeOtherChange();
}
});
Now MainController can be focused on the state of MainView. It should not be aware of ToolbarController or it's buttons.
This sounds like abuse of the controller and model purposes and like a pretty poor event listener setup.
Agreed it would likely be an abuse of model purposes but it is exactly what controllers are for.
This is definitely not an event-listener setup, but that does not seem a good fit in your case. The toolbar you describe seems to exist only to interact with the main view, so it makes sense to have a toolbar controller that depends on main and interacts with it directly.
If the components are truly decoupled then an observer (Pub/Sub) pattern might be more appropriate. See How to fire an event to Ember from another framework if that's of interest.
Let's imagine you have a fullscreen C++ desktop application that consists of several screens with each of them having a distinct function and a ViewController with appropriate models as well. For example a set of the following very simplified screens:
Quiz: The user is navigated through a set of multiple-choice questions.
Quiz Results with Statistics.
Information: The user is presented with information about a specific subject.
Menu (Quiz, Information, Exit)
Judging by the GRASP principle Information Expert, each ViewController will know best when it is finished and time to move to a new screen. Yet by the same principle, it is not the right place to decide what the next screen should actually be. In this rather simple example, one could argue it would be okay but in a more complex application, it will undoubtedly lead to duplicated code and logic as well as higher coupling and lower cohesion. There is also the problem that you would have to create the new widget and controller within the current screen's ViewController which brings all sorts of new problems and at least by the Creator principle, it is not the right choice. You would have to introduce a Factory to alleviate some of the problems, amongst other things.
So, the next logical step is to introduce an ApplicationController with the sole responsibility of managing Views and their controllers including the navigation flow from one view to the next.
This still leaves one problem wide open in my opinion: How to signal the ApplicationController that it is time to move to a different screen and hand over the control to that object properly?
One could use the Observer pattern, for example. Yet what if you have an expensive View active at the moment and want that one destroyed once the new screen is active? If the current ViewController signals the ApplicationController that the next screen should go up, it can manage everything up to the point where it would destroy the currently active screen which it cannot do because the current call comes from exactly that object. Apart from several other problems with that approach.
So my question is (and sorry for all the verbose introduction :P): How do you properly implement a navigation flow from one fullscreen widget to a different one with MVC which solves the above problems, splits the responsibility between the View- and ApplicationController and is nicely object oriented in terms of coupling and cohesion?
Sometimes you miss one detail in your thought process and you open up a whole can of problems without even realizing the mistake you made.
In this case the detail was that you can naturally post asynchronous as well as synchronous events. If you have to make sure that you are no longer in the context of the event posting method, post an asynchronous event. Once you receive that event in your handler, you can be sure that the context was left. And for example you can safely delete the object, if you so desire. Naturally the event handler should not be in the context of the same object that you are trying to delete.
For completeness: In Qt, you can specify for each signal/slot-connection you make with connect(), that it should be of type Qt::QueuedConnection. If you raise a signal, it won't be delivered until the control is back to the thread's event loop. Normally, Qt::AutoConnection is used which delivers a signal at the time it is raised (Qt::DirectConnection) if the receiver is in the same thread or falls back to queuing that signal (Qt::QueuedConnection) if the receiver is in a different thread.
In wxWidgets you can queue events with wxEvtHandler::QueueEvent(wxEvent* event) which is available through the Application singleton for example.