I am new to Ember and noticed the following code in my Handlebars:
{{component sec.myCompRef secInfo=sec fields=model.myMap}}
Does Ember have some helper like {{component}}? If yes, how does it work?
I have generally seen custom helper:
{{my-helper}}
But that has the same name component hbs/js backing it?
But I am not aware of {{component}} helper.
Yes, Ember has {{component}} helper which allows you to dynamically choose and render component via component name passed to {{component}} helper.
So, basically the usage is:
{{component componentName}}
You can of course pass arguments and data to it like you would do with casual component declaration:
{{component componentName model=whatever}}
So, in your case:
{{component sec.myCompRef secInfo=sec fields=model.myMap}}
sec.myCompRef is the name of component to render (it has to match some existing component)
secInfo and fields are parameters passed to component
Related
In Angular, if we want to bind a property from outside for a custom component, we have to use "#Input" to kind of allow that property to be set from outside (i.e. from consuming component template)
Does EmberJS also have some sort of similar mechanism OR does it allow binding directly from the template (hbs) without adding/marking anything in the component JS? Is there any difference when it comes to Ember Octane V/s the earlier versions of Ember?
yes it allows binding from outside the component without adding anything to the component js
in the component hbs file
<p>{{#attribute}}</p>
from outside
<MyComponent #attribute="attributeValue"/>
also you can get the binded attribute from component js
#tracked mycomponentAttribute = this.args.attribute;
in the component hbs file
<p>{{this.mycomponentAttribute}}</p>
no you don't necessarily have to add the input tag but you have to declare the property inside the component you are trying to pass the property to.
{{#each model as |post|}}
{{blog-post title=post.title body=post.body}}
{{/each}}
the blog-post component, define a property named title and this should work.
Still pretty new to Ember. I am trying to use the component Ember helper to overwrite a function on the component.
My component looks like:
Ember.Component.extend({
...
getValue() {...}
...
});
I have another component with a template that looks like:
<div>
{{component myComponentName getValue=(action myCustomGetValue)}}
</div>
I would imagine that this would overwrite the getValue function from the original component, but it does not. Is this possible using this helper to do this? Or am I going about this the wrong way?
Yes, you can pass function references to component helpers in Emberjs.
You can call your component through component helper like:
{{component "my-component" getValue=(action "myCustomGetValue")}}
in which case you should define the custom action in your parent component or controller like:
actions: {
myCustomGetValue(){
return "my custom value";
}
}
You can take a look at this twiddle for this usage.
I have a pretty simple question about ember components.
I'm defining my model in my route:
model() {
return this.store.createRecord('player');
},
I'm passing the model to my component in my template:
{{region-picker model=model region="A" regionName=AName teamNameMap=teamNameMap}}
Then, inside my component, I can succesfully get the correct data from my model, but nothing that I change in my model is getting sent to the database. I can only assume this is because the model is being passed by value instead of by reference. Is there a way to pass it by reference, and if not, what is the common workaround for editing a model inside of a component.
Thanks!
the model should be updated, even if you change the data in the component.
Can you verify the same in the EmberJS plugin ? or did you make an explicit copy in the component.
The other thing you might be missing is you need to trigger model.save()
after your component has updated the model.
Imagine I have a <progress-bar> UI component and I want to create an <app-progress-bar> component that is the same as the <progress-bar> but with some style tweaks.
AppProgressBar.vue:
<template>
<progress-bar class="app-progress-bar"></progress-bar>
</template>
This will break because <progress-bar> requires a percent prop. So I can pass it in:
<progress-bar class="app-progress-bar" percent="percent"></progress-bar>
This seems fine, but is brittle when <progress-bar> has many props. Can I simply pass through all props? Theoretical syntax:
<progress-bar class="app-progress-bar" {...props}></progress-bar>
Which is similar to React/JSX.
Like already answered, extending the component would be a nice way to create your components in this scenario. Similar to class inheritance in other languages.
However, you can also pass an object as prop. If you want to keep things clean, and don't want to extend your components, you could pass a prop like this:
//object with many values that you need to pass. for ex.
myObjectWithSuff: { percent: 10%, someOtherStuff: value, ... }
<progress-bar class="app-progress-bar" myProp="myObjectWithStuff"></progress-bar>
Inside the progress-bar component, declare the prop myProp. Then you can access any properties on that object. For example: this.myProp.percent.
This is just quick and simple, but depending on your architecture, extending components may be the way to go.
After researching this, I don't think it's possible. Instead, you can extend a component and put an extraClass key on data or computed:
import ProgressBar from './ProgressBar'
export default {
extends: ProgressBar,
data() {
return { extraClass: 'app-progress-bar' }
}
}
ProgressBar.vue:
<template>
<div class="progress-bar" :class="extraClass">...</div>
</template>
This isn't as clean or flexible as React, but it works.
Abstract
This can be done by binding $attrs to the child. So in your case it would be:
<template>
<progress-bar v-bind="$attrs" class="app-progress-bar"></progress-bar>
</template>
Link to Docs
vm.$attrs: Contains parent-scope attribute bindings (except for class and style)...and can be passed down to an inner component
Source: Vue $attrs documentation
Example
Let's say that a component called <inner> has a boolean prop called dark and a string prop called color.
Now let's define an outer component called <outer> which wraps <inner> in its definition.
<template>
<inner></inner>
</template>
.
.
.
name: 'outer'
The issue here is if we use the <outer> component, we can't transfer the dark and color props through to the <inner> component which knows what to do with them. For example <inner dark color="blue"></inner> works, but <outer dark color="blue"></outer> doesn't.
If you change the definition of <outer> to include the $attrs binding, it will transfer all the props for you (this doesn't include style and class). Here is the new definition:
<template>
<inner v-bind="$attrs"></inner>
</template>
.
.
.
name: 'outer'
Now you can use <outer dark color="blue"></outer>.
Additional note
You can also use the inheritAttrs prop to prevent this behaviour by default. You can take a look at the official Vue docs here and I also found this JSFiddle which gives an example of inheritAttrs being used.
In anticipation of Routable Components coming soon, I'm attempting to use Components wherever possible in my Ember 2.0 application. I'm running into a confusing issue where I cannot access the parent component's properties from the template when provided in block form. It may be very well that this isn't possible, but wanted to be sure. Here's an example:
Template:
// templates/approvals.hbs
{{#x-secure/schedule/approvals}}
{{#x-secure/layout/full sectionTitle=sectionTitle sectionDescription=sectionDescription}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Component Template:
// templates/components/schedule/approvals.hbs
{{yield}}
Component:
// components/schedule/approvals.js
import Ember from 'ember';
export default Ember.Component.extend({
/////////////////////////////////////
// PROPERTIES
/////////////////////////////////////
sectionTitle: 'Scheduling: Approvals',
sectionDescription: 'Lots of magical , fanstastic stuff.'
});
The issue I'm having is that I'm unable to access sectionTitle and sectionDescription from the parent component (approvals) and pass it into the layout/full component. However, if I remove code from the block of the component and move it to the templates/components/schedule/approvals.hbs template, it works as expected. Is it just not possible to access a parent component's properties from the block form of a component?
Thanks!
It is not possible indeed. The component's properties are available in the component's template, but not in the template that instantiates the component.
If you need the component to make things available, it should yield them explicitly:
// templates/components/schedule/approvals.hbs
{{yield sectionTitle sectionDescription}}
And using the component:
// templates/approvals.hbs
{{#x-secure/schedule/approvals as |title description|}}
{{#x-secure/layout/full sectionTitle=title sectionDescription=description}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Notice the as |x y ...| notation to assign names to yielded values.
Anything may be yielded this way, including this (be careful with that though, that breaks encapsulation) and actions.