calling a helper from another helper - ember.js

I have a template that includes a background image for it's items:
{{#each model as |item|}}
<div style="background-image: url('img/backgrounds/{{item.imageBackground}}');">
{{image.title}}
</div>
{{/each}}
This of course is no good, as binding to style-attribute is deprecated.
So I made a computed property on my controller that serves a htmlSafe string to bind, which is working as intended.
Since I need this - and images bound to a special link - in several templates I made 2 helpers that I want/tried to combine:
The first helper is working perfectly in several other templates (generates a params-string/link to a php-file that serves the desired image)
// helpers/imagelink.js
export default Ember.Helper.extend({
empty: "img/dummies/blank.png",
compute(params, hash) {
if(params[0]) {
let paramString = 'file='+params[0]+'&itemType='+hash.item+'&type='+hash.type;
return ENV.ajaxPrefix + ENV.apiNamespace + '/getimage?'+paramString;
} else {
// display dummy
return this.get('empty');
}
}
});
Now I wanted to make a second helper that somehow encapsulates the first helper and adds the needed 'style' string to the link:
// helpers/imagebackgoundstyle.js
import Ember from 'ember';
import { imagelink } from 'my-app-name/helpers/imagelink';
export default Ember.Helper.extend({
compute(params, hash) {
// ERROR HERE
let link = imagelink(params, hash);
return Ember.String.htmlSafe("background-image: url('"+link+"');");
}
});
calling that seceond helper like this:
<div style={{imagebackgroundstyle workgroup.imageBackground item='workgroup' type='imageBackground'}}>
The error I get here is imagelink.imagelink is not a function.
I've tried several variations, even odd stuff like imagelink.compute(params, hash), ...
Clearly I'm doing something wrong when importing the helper, but I just can't get around what....?
I've tried/viewed
Ember js use handlebars helper inside a controller?
and
Calling a handlebars block helper from another helper
and several more....
Didn't solve/are outdated.

I believe your is not a function errors are all related to your import syntax:
import { imagelink } from 'my-app-name/helpers/imagelink';
You are trying to import something that doesn't exist, as the imagelink helper is exported as default. So you'll have to use:
import imagelink from 'my-app-name/helpers/imagelink';
But you'll run into another problem with your code, so I would recommend changing it to this:
import Ember from 'ember'
import ImageLink from './image-link'
export default ImageLink.extend({
compute(params, hash) {
const val = this._super(params, hash)
return val + '2'
}
})
What you're doing here, is just extending the other helpers, calling it's compute function by using this._super(), and using the return value from that in your new helper.
Here is a twiddle with a working example.

Related

Ember tooltip pass variable

I am building an Ember tooltip module to create dynamic content on hover.
<div class="custom-tool-wrapper">
{{#custom-tool-tipster
side="right"
content=(or getContent question.id)
contentAsHTML=true
class="tool-tipster-field"}}
Preview
{{/custom-tool-tipster}}
</div>
in the ember controller - the function doesn't return the variable "question.id" --- it comes back as 0 always - when it should be a string "q-1"
export default Ember.Component.extend({
getContent(tips){
console.log("tips1")
console.log("question", tips);
},
});
I think what you're actually trying to achieve is best done via computed property on the question model object (your question is still really vague).
content: computed('id', function(){
//this.tips is a part of the model object
//compute and return whatever the content is
return "content";
}
and then just say:
{{#custom-tool-tipster
side="right"
content=model.content
contentAsHTML=true
class="tool-tipster-field"}}
Preview
{{/custom-tool-tipster}}
If you needed to actually invoke a function (which it's rare to think of an instance where the computed property isn't a better solution whenever state is involved), you would use a custom handlebars helper.
(or a b) is (a || b) and isn't function invocation like you're attempting if you're using the ember truth helpers lib for the or helper. It looks like you're trying to accomplish what ember-invoke allows
import Ember from 'ember';
import { helper } from '#ember/component/helper';
export function invokeFunction([context, method, ...rest]) {
if (typeof context[method] !== 'function') {
throw new Error(`Method '${method}' is not defined or cannot be invoked.`);
}
return Ember.get(context,method).apply(context, rest);
}
export default helper(invokeFunction);
which can be used like content=(invoke this "getContent" question.id) to invoke and return the value of a function on the passed in context object (the controller if this in the case of a route's template). Let me be clear, I think this invoke approach is a terrible idea and really gets rid of your separation of concerns and I'm not advocating that you do it. Templates shouldn't contain your logic and definitely shouldn't be calling arbitrary functions on the controller when you have such a nice facility like computed properties.

Ember.js: access the current component from helper

Looking for any solution (even dirty hacks) to access the current component from a custom helper.
import Ember from 'ember';
export default Ember.Helper.extend({
compute() {
... who is computing me? ...
}
});
Simply pass this to the helper.
Say the code example you mentioned is for format-currency helper, so you can pass the context like {{format-currency value this}}
And in the helper you can access it like:
import Ember from 'ember';
export default Ember.Helper.extend({
compute([value, container]) {
//... who is computing me? ...
// container is computing you
}
});

helper returning an object not scalar value expected

Thought I'd create a if-then-else helper which looks like this:
import Ember from 'ember';
export function ifThenElse(param, ifTrue, ifFalse=null) {
return Boolean(param) ? ifTrue : ifFalse;
}
export default Ember.Helper.helper(ifThenElse);
And then when using my component I can do the following:
{{my-nifty-component class=(if-then-else pigsHaveWings 'wake-up' 'good-answer') }}
Well the problem I'm having is that my helper is not very helpful and the HTML/DOM result is:
<div class="[object Object] ember-view"></div>
Can someone please help me with my helper?
In modern versions of Ember helper function gets one parameter and this parameter is an array of parameters, passed to helper. You need to rewrite your helper as
import Ember from 'ember';
export function ifThenElse([param, ifTrue, ifFalse]) {
return Boolean(param) ? ifTrue : ifFalse || null;
}
export default Ember.Helper.helper(ifThenElse);

Ember CLI: custom input helper

I'm trying to extend Ember's TextField with UrlField so that if someone forgets to include http://, it does it for them.
Here's my View:
views/input-url.js
import Ember from 'ember';
export default Ember.TextField.extend({
type: 'url',
didInsertElement: function() {
this._super.apply(this, arguments);
this.formatValue();
},
onValueChange: function() {
this.formatValue();
}.observes('value'),
formatValue: function() {
var pattern = /^https{0,1}:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+/g;
if (pattern.test(this.get('value')))
return;
if (!pattern.test('http://' + this.get('value')))
return;
this.set('value', 'http://' + this.get('value'));
}
});
If I use it in my template like this, it works fine:
{{view "input-url" value=url}}
I prefer to use custom view helpers, so I created this (following the guide at the bottom of this page: http://guides.emberjs.com/v1.11.0/templates/writing-helpers/):
helpers/input-url.js
import Ember from 'ember';
import InputUrl from '../views/input-url';
export default Ember.Handlebars.makeBoundHelper(InputUrl);
Now trying to render this in my template doesn't work:
{{input-url value=url}}
I've also tried different permutations of this, including what's shown in the guide Ember.Handlebars.makeBoundHelper('input-url', InputUrl); (which throws an error), but I can't seem to get my input field to show up. What am I doing wrong?
Not sure what you are doing wrong with your view helper, but there is a much simpler solution: take advantage of the fact that Ember.Textfield is a component. http://emberjs.com/api/classes/Ember.TextField.html
Simply move views/input-url.js to components/input-url.js and get rid of your view helper.
Then {{input-url value=url}} should work automatically.
If you want do to this using a helper, you cannot extend Ember.TextField because extends Ember.Component and is not a Handlebars helper.
The way to do this using a helper would actually be simpler. Since you are using Ember-CLI, you can create a helper called "input-url" with the command ember g helper input-url and the only code you would need is the code within your formatValue() function:
helpers/input-url.js
// define patter globally so it's not recreated each time the function is called
var pattern = /^https{0,1}:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+/g;
export function inputUrl(value) {
if (pattern.test(value)) {
return value;
}
if (!pattern.test('http://' + value)) {
return value;
}
return 'http://' + value;
};
export default Ember.Handlebars.makeBoundHelper(inputUrl);
And you can use it like:
{{input-url PASS_YOUR_URL_HERE}}
Where the value you pass will be the value of the value variable within the helper.
You could also create a component, as #Gaurav suggested, using the exact code you have above, just in components/input-url.js instead and delete the helper cause it is not necessary anymore. You also have to edit the corresponding template of the component if you want it to display the value with a single handlebars expression:
templates/components/input-url.hbs
{{value}}
The usage with a component would be:
{{input-url value=PASS_YOUR_URL_HERE}}

Computed Property Macros with Ember CLI

I'm attempting to DRY up my application and move some functionality into macros with Ember CLI. After reading this article, I thought I could get things working but I'm getting an undefined is not a function TypeError: undefined is not a function error when trying to use the macro with any arguments. If I don't pass any arguments, ember doesn't throw the error. To generate the file I'm using the command ember generate util calc-array
// utils/calc-array.js
import Ember from 'ember';
export default function calcArray(collection, key, calculation) {
return function() {
...
}.property('collection.#each');
}
// controller/measurements.js
import Ember from 'ember';
import calculate from '../../utils/calc-array';
export default Ember.ArrayController.extend({
high: calculate(this.get('model'), 'value', 'high'),
...
});
this.get('model') causes the problem - this points to global object, not controller instance. Pass the string (i.e. model) and use this.get inside computed property.
Also collection.#each will not work, it's not a valid path.
Summing it up:
export default function calcArray(collectionPath, key, calculation) {
return function() {
var collection = this.get(collectionPath);
...
}.property(collectionPath + '.#each');
}