How do I pass an object to a component? - ember.js

This question was asked in the Ember Discord.
Is there a way to pass an object into a component
<SomeComponent object={color: green} />

Using latest Ember from October of 2021
yes! two ways,
via template-only:
<SomeComponent #object={{hash color="green"}} />
This uses the hash helper.
Note that there are other differences here as well.
Arguments must begin with # (to differentiate from attributes), which are native HTML, such as class, id, etc -- and "dynamic bits" in ember are surrounded by double curlies, {{}}. For an overview of attributes and arguments, I recommend Component Arguments and HTML Attributes from the official guides.
The second way to pass an object to a component is if you already have one:
import Component from '#glimmer/component';
export default class MyComponent extends Component {
myObject = { color: 'green' };
}
<SomeComponent #object={{this.myObject}} />

Related

Ember helper has undefined values

I can't figure out why the helper I created in app/helpers/shop-name.js:
import { helper } from '#ember/component/helper';
export function shopName(shop) {
return shop.identifier + '-' + shop.name;
}
export default helper(shopName);
doe not format the values as needed in my template application.hbs:
<select>
{{#each model as |shop|}}
<option value={{shop.id}}>{{shop-name shop}}</option>
{{/each}}
</select>
I get undefined-undefined displayed in my select list.
If I do the same conversion directly in the template:
<option value={{shop.id}}>{{shop.identifier}}-{{shop.name}}</option>
it works as needed. What's wrong with that helper ?
I'm using Ember 3.0.
Thank you.
I'm not sure that helpers can take inputs that aren't strings. I hope someone can confirm.
Anyway, your use case is really better handled at the model level, using a computed property:
shopName: computed('identifier', 'name', function() {
return `${identifier} - ${name}`;
})
and then you would just use {{shop.shopName}} in your template
There are multiple issues in your code.
shop is an object and I bet it's an Ember.Object. If dealing with Ember.Objects, you should always use get() method to retrieve values for now. This might change soon. Since you are not using get() method, you get undefined. Embers template engine is using getter and therefore it's a huge difference between shop.identifier in your helper and {{shop.identifier}} in your template. Resolve the issue by shop.get('identifier') or import { get } from '#ember/object'; [...] get(shop, 'identifier'); Use the last one if you can't be sure that shop is an instance of Ember.Object.
Since you are passing shop and not shop.identifier as argument to your helper, the helper will only be recomputed if the reference of shop changes (e.g. replaced by another object). Changes to the value of it's properties will not recompute the helper.
One solution for you problem is to use a computed property as suggested by ctcpip. Another one is the concat helper: {{concat shop.identifier '-' shop.name}}.

How can I write custom handlebars in Ember js?

js:
Ember.Handlebars.registerHelper('demo-helper', function (property, options) {
console.log('demo helper');
return '<li><a> '+ msg+'</a></li>';
});
hbs :
{{#demo-helper}} msg {{/demo-helper}}
How can I wrap hi (inner html of handlebar) with any custom tag?.I know that component has tag parameter that encapsulates html with given tag. But it supports only one tag. I want to encapsulate with multiple tags like
<li><a>innerhtml</a></li>
This is what components are for. They have can have an arbitrarily sized template. You can use {{yield}} to nest other html inside the component.
See https://guides.emberjs.com/v2.15.0/components/wrapping-content-in-a-component/.
component template:
<div><div><div>{{yield}}</div></div></div>
usage:
{{#my-component}} Hello world {{/my-component>}}
Components are very powerful and have many other options, see the guides for more info. They are the best resource.

How to render Ember component on same line

This Ember template code...
<div class="detail bedrooms">
<span>Number of bedrooms:</span> {{rental.bedrooms}}
</div>
<div class="detail temperature">
<span>Current Temperature:</span> {{city-temperature location=rental.city}}
</div>
...results in this rendering...
How could one get the "25C" text to render on the same line as "Current Temperature" in the same way that "San Francisco" is on the same line as "Location"?
I have tried getting the city-temperature component to return only text, but (secondary question here) is it even possible for a component to return only text if the text is from a remote ajax request since the promise seems to need a DOM element to append to?
This Ember app is available here on GitHub. It's a modified version of the official Ember.js tutorial app with this "Current Temperature" detail added.
The problem is that; city-temperature is a component; and by default ember components is assigned div as their tags. Due to this; the content of city-temperature starts at a new line.
What can you do? Play with css and make sure div tag of city-temperature does not start at a new line; but instead floating right within the owner div. The second option is making city-temperature component tagless; so that it does not start at a new line. To achieve that; you can just declare:
tagName: ''
within city-temperature.js. You can see the following twiddle to see the second option I mentioned.
After reading your comments; I have forked your repository and made minor modifications to achieve what you want. You can check my repository and my corresponding commit to see what I changed.
Basically; I really did not like the idea that weather service is returning a DOM element. I have changed it to return an instance of ember promise (I mean Ember.RSVP.Promise). Within city-temperature.js I just set a retrieved weather value from weather service and set it to the component instead of making DOM modification. Finally; I modified city-temperature.hbs to render weatherValue and added a simple css item in order to prevent weather-container div to insert a new line break.
I am not sure; whether you will like my solution or not; but I guess you will retrieve the visual appearance you want. You can always rely on promises for async tasks; you do not need to create DOM elements and pass them around to components and append them to somewhere within DOM tree of the corresponding component. I believe one of the reasons we are making use of a framework like Ember is to prevent such DOM modifications. Best regards.
An ember component adds a div by default. If you don't want to add this div tag, you need to set tagName to an empty string.
That's called tagless component.
Further, for your case, you can give a tagName='span' so you will not need a span in your template.
export default Ember.Component.extend({
tagName: 'span',
classNames: ['weather-container'],
weather: Ember.inject.service(),
//rest of your code...
});

How can I determine if a component returns nothing dynamically?

I have a ember component that is accessing a database and returning the results in a datatable type UI component. I would like to be able to use "N/A" when the result of the component is null or nothing.
For example, I have:
{{each bar in foobars}}
<td class="classyTD">
{{getBars bar=bar}}
</td>
{{/each}}
This works great when I have data, but returns nothing when I don't have data. The designers would prefer an "N/A". Modifying the database isn't an option and while modifying the component getBars is an option, it will be extremely painful.
Is there a method/way to handle this after the execution of the component? If not, or if it's a terrible idea - I'll suffer through changing the component, I trust the community's opinion.
You really should do this inside the component template. You can give the N/A string as a parameter, if that's of any help: http://emberjs.jsbin.com/lemabekuwi/2/edit?html,css,js,output
Or you could change the component that it indicates emptiness through a class and use some css magic: http://emberjs.jsbin.com/duqazahegi/1/edit?html,css,js,output
If you want to limit logic in the handlebars you can have the following in the component js file:
({
setBar: (function() {
if (!this.get('bar')) {
return this.set('bar', 'N/A');
}
}).observes('bar')
});

how to use custom templates for same classes in field plugin, grails

For example I have 2 class:
Person {
String name
String descriptionOfPerson
}
Company {
String name
String descriptionOfCompany
}
Normally, fields plugin will use the same template /_fields/string/_field.gsp for all of them.
But I want to use /_fields/string/_field.gsp template for String name and /_fields/ckeditor/_field.gsp (to render ckeditor) for descriptionOfPerson and descriptionOfCompany
Is it possible to do like that? And how ?
As far as I know the plugin and it's documentation this is not possible without modifications on your side.
The default order in which the plugin searches for the templates is:
grails-app/views/controllerName/actionName/propertyName/
grails-app/views/controllerName/actionName/propertyType/
grails-app/views/controllerName/actionName/ (not applicable here)
grails-app/views/controllerName/propertyName/
grails-app/views/controllerName/propertyType/ (not applicable here)
grails-app/views/controllerName/ (not applicable here)
grails-app/views/_fields/class/propertyName/
grails-app/views/_fields/superclass/propertyName/
grails-app/views/_fields/associationType/ (not applicable here)
grails-app/views/_fields/propertyType/
grails-app/views/_fields/propertySuperclass/ (not applicable here)
grails-app/views/_fields/default/
Neither is there a possibility to pass a custom template, nor to distinguish between between the property name when the template is found via the property's class.
However, there are some possibilities to realize your case.
A) Template condition
You could include a condition into the /_fields/string/_field.gsp template and make use of the provided template parameters, such as:
<g:if test="${property == 'descriptionOfPerson' || property == 'descriptionOfCompany' }">
<!-- render your ckeditor here or include another template via g:render -->
</g:if>
<g:else>
<!-- render your normal input here or include another template via g:render -->
</g:else>
B) Template for each controller property
for instance:
grails-app/views/_fields/person/description/ and
grails-app/views/_fields/company/description/.
Both templates could include another template via g:render, being place at grails-app/views/_fields/ckeditor/
Applying this to your example, you could place the template in any of the bold paths and it would be preferred to the /_fields/string/_field.gsp template.
Personally I would stick with B), which allows a more fine-grained control and is easier to understand for others that are not familiar with your code. I would also rename both of your fields to description. As always, this decision depends on your complete application and its overall complexity.
The default rendering mechanism of the fields plugin (without using the override template stuff) uses the widget constraint of grails to render a textarea instead of a <input type="text" /> (as you see in the source of the fields plugin).
With an unobtrusive javascript library (ckeditor seems to have these capabilities as described in their dev guide), that replaces a <textarea> with something more exciting, the only thing you have to do is the following:
Person {
String name
String descriptionOfPerson
static constraints = {
descriptionOfPerson widget: 'textarea'
}
}
Company {
String name
String descriptionOfCompany
static constraints = {
descriptionOfCompany widget: 'textarea'
}
}
At least now (2 years after initial post) I found 2 working solutions for me:
(1) specify for every field I want to have wysiwyg editor functionality a sub-folder in views (like grails-app/views/myDomainObject/myWysiwygField/_wrapper.gsp) with the following content:
<div class="fieldcontain${required ? ' required' : ''}">
<label for="${property}">${label}<g:if test="${required}"> <span class="required-indicator">*</span></g:if></label>
<ckeditor:config var="toolbar_Mytoolbar">
[
[ 'Undo', 'Redo', '-', 'Bold', 'Italic', '-', 'NumberedList', 'BulletedList']
]
</ckeditor:config>
<ckeditor:editor name="${property}" width="40%" toolbar="Mytoolbar">${value}</ckeditor:editor> <!-- toolbar="Basic" -->
</div>
(2) declare the wysiwyg fields as textarea
static constraints = {
myWysiwygField widget: "textarea"
}
and define (once) grails-app/views/_fields/textarea/_wrapper.gsp with the same content than above.
So the first one is more fine-granular while the second one is more appealing for simpler problems.