Ember js use handlebars helper inside a controller? - javascript

I have a helper method that maps a number to a text -
Ember.Handlebars.helper('getStatusText', function (value, options) {
switch(value) {
case 1: return "Fresh";
break;
case 2: return "Callback";
break;
default: return "Unable to get Status";
}
});
I am able to use the helper in the view by using {{getStatusText 1}}
But how do I use the helper in an action inside an ObjectController ?
Test.DealController = Ember.ObjectController.extend({
selectedStatusType: null,
statusList: ["Fresh","Callback"],
actions: {
updateStatus: function(deal) {
// How do I call the handlebars helper here ?
console.log({{getStatusText 1}});
}
},
});
this obviously does not work.
What are the other ways ??
For better understanding, here is the jsbin

With ember-cli it can be done like this:
// helpers/foo.js
export function foo(params) {
return params;
}
export default Ember.Helper.helper(foo);
Helper foo exports a function (containing the helper logic) and the function wrapped in an Ember helper (for use in a template).
// helpers/bar.js
import { foo } from '<project>/helpers/foo';
export function bar(params) {
return foo(params);
}
export default Ember.Helper.helper(bar);
Helper bar imports the helper function from foo and uses it in it's own template helper.

Pull the logic out of the helper, making it available to be called both by the helper, and by non handlebars helper items alike. Parsing it into handlebars template and evaluating it is over complicating it.
Where you put it is up to you, you could namespace it to your app, or just create it as a function that lives with your helper.
function getStatusText(value){
switch(value) {
case 1: return "Fresh";
break;
case 2: return "Callback";
break;
default: return "Unable to get Status";
}
}
Ember.Handlebars.helper('getStatusText', function (value, options) {
return getStatusText(value);
});
http://emberjs.jsbin.com/cenep/1/edit

Most helpers are simple. In this case, exporting a vanilla function is the way to go. Pass the function exactly the data it needs.
Ember also has class-based helpers for a more complex use case. They can leverage other container dependencies. You can still have a class-based helper's compute method call your exported vanilla function.
However, the parameter list to the function could get unwieldy for other callers.
import Helper from 'ember-helper';
import service from 'ember-service/inject';
export function vanillaFunction(imageService, userService, foo, bar, baz, dependency3, options) {
...
}
export default Helper.extend({
imageService: service('image'),
userService: service('user'),
compute(positionalParams, hashParams) {
return vanillaFunction(this.get('imageService'), this.get('userService'), positionalParams[0], positionalParams[1], ...);
},
});
To benefit from container lookups, rather than call the vanilla function, you can manually instantiate such a helper and call compute yourself. This is rare. But it benefits from a small interface, uniform with how you'd call it in the template. The helper is normally instantiated by HTMLBars for use within a template, which has special context and observation rules. So there's a little hoop jumping to use it inside your e.g. controller.
import Controller from 'ember-controller';
import getOwner from 'ember-owner/get';
import setOwner from 'ember-owner/set';
export default Controller.extend({
someMethod() {
const owner = getOwner(this);
const helperFactory = owner.resolveRegistration('helper:my-helper');
const helperInstance = helperFactory.create();
setOwner(helperInstance, owner); // I'm not sure why the factory doesn't do this for you
return helperInstance.compute([1]); // "Fresh"
},
});

Related

Can I use OOP in Redux Store actions

I'm using React JS and Redux JS.
I know that Redux actions functions should be a pure function
But, I was reading about Object Oriented Programming (OOP) and its benefits.
So, Can I use OOP in Redux action (Class) instead of pure functions ??
For example:
If I've a state named "articles", It may have actions like:
function getArtcles() { ... }
function addArtcle({data}) { ... }
function deleteArtcle(id) { ... }
......
So, can I replce that with:
class Artice {
getArtcles() {...}
addArtcle() {...}
deleteArtcle() {...}
}
??
I assume those functions are action creators? Ie, they return an object with a type property, and possibly some additional data?
function deleteArticle(id) {
return { type: 'deleteArticles', id };
}
Or they may create thunks:
function deleteArticle(id) {
return async function (dispatch) {
dispatch({ type: 'deleteStarting' });
// do something asynchronous
dispatch({ type: 'deleteSuccessful' });
}
}
If so, yes, you can put them in a class if you want. I don't see a benefit to putting these in a class, but redux doesn't even have a way to know how you created the actions, so it won't mind.

Vue.JS import custom module

I have created a module greatings.js like this one:
function greatings() {
this.hello = function() {
return 'hello!';
}
this.goodbye = function() {
return 'goodbye!';
}
}
module.exports = greatings;
Then I imported it into main.js in VUE.JS just like:
import greatings from './assets/js/greatings';
Vue.use(greatings);
Now I would like to use it in my components but if I do it I got an error:
mounted() {
this.greatings.hello();
}
ERROR: Error in mounted hook: "TypeError: Cannot read property 'hello' of undefined"
How to fix it and be able to use my greatings?
Thanks for any help!
greatings.js file should be like this
export default {
hello() {
return "hello";
},
goodbye() {
return "goodbye!";
}
};
and import in any file you want to use like this
import greatings from './assets/js/greatings';
and call any function do you want. remove this function Vue.use(greatings);
When using Vue.use() to register a custom plugin, it has to define an install() function, which is called by Vue. From docs:
A Vue.js plugin should expose an install method. The method will be called with the Vue constructor as the first argument, along with possible options.
See the provided example, for all the options you have when creating a custom plugin: https://v2.vuejs.org/v2/guide/plugins.html

How to add global funtions like Create, Methods, Mounted from a plugin in Vue?

For some cases, I don't want to use mixins in my Plugin.
I am trying to add a custom Methods like created(), mounted(), methods{}, so I can access its property when the component is loaded & run my custom method.
example: "customMethod"
// #root/home.vue
<script>
export default {
name: 'Home',
props: {
msg: String
},
mounted(){
//
},
methods{
//
},
customMethod{
}
}
</script>
.vue file
<script>
export default {
customMethod () { // Custom option.
console.log('custom method executed!')
},
methods: {},
created () {},
mounted () {}
}
</script>
plugins/customMethods.js
const customMethods = {
install (Vue) {
// INSTALL
if (this.installed) return
this.installed = true
Vue.options = Vue.util.mergeOptions(Vue.options, {
created: function () {
if (this.$options.customMethod) { // If found in the component.
this.$options.customMethod() // Execute the customMethod.
}
}
})
}
}
export default customMethods
main.js
import customMethods from 'plugins/customMethods.js'
Vue.use(customMethods)
What this does is extend the default options for all Vue instances so
the behavior is applied to every single Vue instance created. This is
however undocumented at the moment.
Alternatively, this can also be achieved by the use of global mixin in the plugin. (which you don't want for some reason as per your use case.)
Moreover, one advanced use case is we may need special handling when merging custom option values during Vue.extend. For example, the created hook has a special merge strategy that merges multiple hook functions into an Array so that all of them will be called. The default strategy is a simple overwrite. If you need a custom merge strategy you will need to register it under Vue.config.optionMergeStrategies:
Vue.config.optionMergeStrategies.customMethod = function (parentVal, childVal) {
// return merged value.
}
Every component can access your customMethod if you inject it into Vue.prototype like this:
Vue.prototype.customMethod = function() {}

Testing Vue components methods

I am starting with Vue.js and is really hard to find documentation about Unit Test.
I am trying to test components methods and builtin stuff as ready(). I can call those correctly but they internally have references to this object and this context is lost during testing time.
error
TypeError: this.$on is not a function
spec.js
import Vue from 'vue';
import Partners from 'components/main/partner/Partners';
describe.only('Partners.vue', () => {
it('should render with mocked partners', (cb) => {
Partners.ready(); // I get an error here because ready() is calling inside: this.$on(...)
cb(null);
});
});
component.vue
export default {
name: 'Partners',
data() {
return { };
},
methods: {
get() {
// ...
}
},
ready() {
this.$on('confirm', (confirm) => {
// ...
});
this.get();
}
};
I think ready() is depreciated along with Vue 1.0. Consider upgrading to Vue 2 (where mounted() replaces ready()).
To test your component, you need to initialize your component and a Vue instance, (and usually mount it, depending what you are doing).
Using the vue-webpack template (which uses Vue 2):
var ctor = Vue.extend(Partners)
var vm = new ctor()
vm.$mount()
now you can do things like vm.method(), and vm.mount() will automatically be called, etc.

How to get current route path in helper in Ember?

I'm creating a helper and I need to access the current route name. I'm using Ember CLI with ES6 and so I don't have access to App object.
Here's what I have.
import Ember from 'ember';
/**
* {{route-active 'route' ['stringIfActive' ['stringIfNot']]}}
*/
export function routeActive(params/*, hash*/) {
var currentRoute = null; // we need this
if( ! params.length ) {
return;
}
return currentRoute === params[0] ?
params[1] || 'active' :
params[2] || '';
}
export default Ember.HTMLBars.makeBoundHelper(routeActive);
With the shift to components, it is harder to get route name. The best way is to add an initializer such as
ember g initializer router
(from command line), and
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
in a initializers/router.js. You can also inject into controller if you need to. Then just do simply
this.get('router.currentRouteName');
in JS, or
{{router.currentRouteName}}
in template.
This is the only way I have found to get it reliably, and observable in Ember 2.4
Although it's a private API, I've been known to use and abuse the container for when I need it. The application controller has the currently active route as a property called currentPath.
import Ember from 'ember';
export default Ember.Handlebars.makeBoundHelper( function(value, options) {
var appCtrl = this.container.lookup("controller:application");
return appCtrl.get('currentPath');
});
This assumes you wanted your helper to simply return the value. If you need to return something else, well at least I got you this far I'm sure you can take it from here.

Categories