In a component it is really easy to have an optional action provided to the component. In the JS of the component I can write:
if (this.get('someAction')) {
this.sendAction('someAction');
}
In my application route I have a 'generic action' that saves me providing widget components with long lists of actions, it looks like this:
genericAction: function(customActionName, customActionParams) {
this.send(customActionName, customActionParams);
}
For various reasons (including using genericAction in some components to fire an action a test could subscribe to, but the app not necessarily use in some hard to test async/pretender workflows) I would prefer to check the action exists, i.e:
genericAction: function(customActionName, customActionParams) {
if (this.get(customActionName)) {
this.send(customActionName, customActionParams);
}
}
Similar to how you can in a component, however this does not work, nor does this.controller.get(customActionName).
Other than keeping a hard coded list of actions, how can I achieve this?
If you keep your actions in the routes/application.js file itself then the code would be
In Ember 2.0 or later:
if(Em.get(this.actions, actionName)) {
this.send(actionName);
}
In Ember 1.13
this.actions is undefined in Ember 1.13, you have to use this._actions
if(Em.get(this._actions, actionName)) {
this.send(actionName);
}
If you need to support both Ember 1.x and 2.x then use something like:
let actions = this.actions || this._actions;
if(Em.get(actions, actionName)) {
this.send(actionName);
}
If you keep your actions in the application controller (controllers/application.js) then siva - abc's answer works great.
You can check for the actions in controller.actions. In your case, you have to check as
if(Em.get(this.controller.actions, actionName)) {
this.get('controller').send(actionName);
}
Here is a demo
if you are in a component you can use
if (this.get('yourActionName')) { }
Related
I've been experimenting with GraphQL/Apollo in Typescript and run into a huge amount of really obvious typing issues that make me wonder whether I'm just doing it wrong.
Effectively, what I'd like to do (since it makes sense to me) is compose the data for my components by plastering in multiple HOC calls. Here's an example of what I mean (sans imports)
interface Props {
theme: Theme;
}
class Navigation extends React.Component<Props & ViewerProps & PageProps> {
render() {
const { viewer, page } = this.props;
return <h1>Hello, {viewer.name}; welcome to {page.name}</h1>;
}
}
export default withPage(withViewer(Navigation));
Elsewhere:
<Navigation theme={this.theme} />
Is this actually a pattern of any kind in Apollo? Are there any references out there which use this, or something similar?
Thanks
In Apollo 2.1 they upgraded to use render prop functions.
https://reactjs.org/docs/render-props.html which will help with stronger typing through the components.
https://dev-blog.apollodata.com/introducing-react-apollo-2-1-c837cc23d926
One thing I've been doing with components with HoCs is to cast the HoC with React.ComponentType so that it can make sure you're passing in the correct props to the component
const NavigationContainer: React.ComponentType<Props> =
withPage(withViewer(Navigation));
export default NavigationContainer;
I am using Ember 2.2.0
When writing components I used to propagate events from the component to the route (or whatever embeds the component) with this.sendAction(…). I checked out the docs lately and discovered that they advise another approach.
https://guides.emberjs.com/v2.2.0/components/triggering-changes-with-actions/
this.get('action')();
Since Ember is known to be very opinionated I want to stick as much to the best practices as possible. But I am not sure if the docs might be out of date or the tutorials using sendActions are.
So I would like what is the way to do this?
When you are using this.sendAction('actionName') you're bubbling up an action which you'll have to catch on the component/controller with actions
//controller/route/component.js
actions: {
actionName: function() {
//Do something
}
}
If you want to send that up the chain, you'll have to call sendAction('') again on the component/controller and catch it again on the parent (and so on).
The other approach this.get('action')() uses closure actions, which are regular javascript functions. These are the preferred way to invoke actions in Ember 1.13.X as far as I know. One neat thing that closure actions have is that you can have return values. Meaning that you can have something like this:
//a controller
actions: {
saveResult() {
return this.get('model').save(); //notice the return (which returns a promise)
}
}
//some template that uses the controller above
{{a-component save=(action 'saveResult')}} // Passes the saveResult action to the component
//a-component.js
actions: {
someAction: function() {
this.attrs.save().then(() => {
//Do something with the return value
});
}
}
A lot can be written about closure actions, but others have written far better than I could, so I recommend the following articles:
Ember Closure Actions #1
Ember Closure Actions #2
Ember Closure Actions #3
And if you're new to the whole DDAU (Data Down Actions Up) concept, I really recommend Sam's article about the concept in general.
Update: There's also an addon (linked in the comments by #locks) which allows closure actions to bubble to routes. Note that if you look to upgrade Ember to more recent versions (3.8 and up), route-actions will not be compatible.
For those using Ember version >=3.4:
Use closure actions instead of sendAction
sendAction has been deprecated in 3.4. You can find an example of how to define closure actions under the relevant deprecation text:
https://deprecations.emberjs.com/v3.x/#toc_ember-component-send-action
Ember.View is deprecated, in favor of Components. That's great but I'm having trouble making sense of the 2.0 release.
Most often, I used the didInsertElement hook to run some jQuery code etc. But now that the Em.View class has been deprecated, how can I achieve the same thing? I don't want to create a component or anything like that. It doesn't make sense to create components for normal pages(routes). Simply because its not a re-usable thing plus component's scopes are isolated.
Say we have a about route, and when the template is rendered I just want to sun some jQuery code. How can I do this in Ember 2.0+?
You could take advantage of didTransition hook and Ember.run.next. Check my solution:
export default Ember.Route.extend({
actions: {
didTransition() {
Ember.run.next(this, 'initParticles');
}
},
initParticles() {
let ammount = (window.matchMedia('(max-width: 456px)').matches) ? 40 : 100;
particlesJS('particles-js', {
// my options
});
}
});
While it doesn't seem like the best approach, creating a component is probably your best option. Soon we'll have routeable components which will take over much of what controllers and views use to do. Creating a component and just inserting it into your template should put you on a good path to be ready for routeable components.
Is there any component or plugin that I can use, to integrate piwik into an ember application?
It's very simple actually. Sadly I haven't found out yet how I can track the individual views. But this will get you going with a basic set-up that also scales well.
Put this in your ApplicationRoute it hooks the route's didTransition so that every time you transition between routes data gets send to the Piwik server.
actions: {
didTransition: function () {
Ember.run.once(this, function () {
window._paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
_paq.push(['setTrackerUrl', 'https://[Piwik server]/piwik.php']);
_paq.push(['setSiteId', 1]);
});
}
}
And don't forget to put a reference to Piwik.js in your application template to load the Piwik tracking library.
<script src="https://[Piwik server]/piwik.js" async defer></script>
There doesn't seem to be an all-integrated solution for Ember like there is for AngularJS.
You can have a look at this forum thread which may get you started.
You may also want to have a look at how it's working in AngularJS: http://luisfarzati.github.io/angulartics/ or https://github.com/mike-spainhower/angular-piwik
You can use ember-cli-piwik. It's quite simple:
Install the addon:
ember install ember-cli-piwik
Then configure it in your config/environment.js
piwik: {
sid: 123,
url: 'https://your-piwik.endpoint.com'
}
For more information, read the docs.
I would like to have a route substate not show up in the URL, but still be able to take advantage of having a route class on which I can define renderTemplate, model, setupController, etc. hooks. Is this possible with the v2 router? I am using Ember release candidate 2.
Here's an example.
Suppose I have the routes:
/exercise/:exercise_id
/exercise/:exercise_id/correct
/exercise/:exercise_id/incorrect
I would like all of these to show up in the URL as:
/exercise/:exercise_id
As I don't want the student to just directly type in /correct onto the end of the ULR and get to the correct answer. And although I have a way to prevent that from working, the full route still shows up in the URL. From the student's perspective, I only want them to think about the state as /exercise/:exercise_id.
Of course I could just store the state correct vs. incorrect in some controller variable, but then I loose the convenience of having route classes, ExerciseCorrectRoute and ExerciseIncorrectRoute, which I want to behave differently, and so the hooks, like renderTemplate and setupController, are nice to have defined cleanly in separate places.
Thoughts?
Kevin
UPDATE:
I went with Dan Gebhardt's suggestion because I like to keep things as much as possible within the framework's considered design cases, as this seems to reduce headaches given Ember is still evolving. Also I didn't get a chance to try out inDream's hack.
Although I still think it would be nice if the router added a feature to mask substates from the URL.
Every route must be associated with a URL for Ember's current router.
Instead of using multiple routes, I'd recommend that you use conditionals in your exercise template to call the appropriate {{render}} based on the state of the exercise. In this way you can still maintain separate templates and controllers for each state.
You can reference to my answer in Ember.js - Prevent re-render when switching route.
Reopen the location API you're using and set window.suppressUpdateURL to true if you want to handle the state manually.
Ember.HistoryLocation:
Ember.HistoryLocation.reopen({
onUpdateURL: function(callback) {
var guid = Ember.guidFor(this),
self = this;
Ember.$(window).bind('popstate.ember-location-'+guid, function(e) {
if(window.suppressUpdateURL)return;
// Ignore initial page load popstate event in Chrome
if(!popstateFired) {
popstateFired = true;
if (self.getURL() === self._initialUrl) { return; }
}
callback(self.getURL());
});
}
});
Ember.HashLocation:
Ember.HashLocation.reopen({
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
if(window.suppressUpdateURL)return;
Ember.run(function() {
var path = location.hash.substr(1);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(path);
});
});
}
});