Access parent context from ember component block - ember.js

I'm trying to work out how blocks work with ember components. With the following I'd expect project.name to be rendered for each loop.
// components/block-test.js
export default Ember.Component.extend({});
// index.hbs
{{#each project in projects}}
{{#block-test}}
{{project.name}}
{{/block-test}}
{{/each}}
But when I use this pattern project.name is not rendered. Is this the expected behaviour and if so how could I change it to get the above code to work?

component's are intentionally isolated, pass in anything you want to use (you don't need yield if you are passing it in)
{{#block-test project=project}}
--{{project.name}}--
{{/block-test}}
No Component template
--apple--
--dog--
--piano--
With yield
Component template
--{{yield}}--
Using Component
{{#block-test}}
{{project.name}}
{{/block-test}}
--apple--
--dog--
--piano--

Related

In Ember.js how do you extend a component and trigger an action on did-insert?

Our project is currently using Ember 3.12 and we are trying to upgrade to using Ember 3.20, but we are having an issue with extending an ember-power-select component (which now uses Glimmer components). In our extended component we need to call a method when the component is inserted and have access to component element, which we did using didInsertElement in Ember 3.12, but we now need to use a did-insert modifier. However, when we create a template file of our own which contains an element which triggers the did-insert modifier the power-select element is not displayed (because our template file has replaced it). I would rather not copy the entire contents of the power-select.hbs file into our own file and wrap it in a div that contains the did-insert modifier so that we can get access the component element in the action. Is there a pattern for this situation? Like templates can now be extended or there is another way to trigger an action when the component is inserted (and still get access to the component's element)?
I would recommend to not extend a component in Ember Octance by extending from it's JavaScript class. Instead invoke the component in the template of the wrapping component:
{{! app/component/wrapper-around-ember-power-select.hbs }}
<PowerSelect
{{! passthrough all arguments you want to support }}
#selected={{#selected}}
#options={{#options}}
#onChange={{#onChange}}
{{! register your own modifier }}
{{did-insert this.onEmberPowerSelectInsertIntoDom}}
{{! set some HTML attributes }}
class="foo"
/>
// app/component/wrapper-around-ember-power-select.js
import Component from '#glimmer/component';
import { action } from '#ember/object';
export default class WrapperAroundEmberPowerSelectComponent extends Component {
#action
onEmberPowerSelectInsertIntoDom(element) {
// do something with the element
}
}
Extending the component in this way has the benefit of only using its public API.

Adding a class dynamically to hide only yield part of the ember component without wrapping

Is there any way to add a class(In my case it is hide class) dynamically to hide(display : none) only yield part of the ember component without wrapping the yield part of the component with tags (div,span or anyother) in Ember JS?
My Case :
{{#if isLoading}}
<p>Loading....!</p>
{{/if}}
<div class="{{isLoading "hide"}}">
{{yield}}
</div>
Here I want to hide the yield part without wrapping it up by a div tag
Note : I can't use If Else statement as it will destroy the current instance of component every time isLoading property changes. This is a component to show loading. Any other ways to use a component for loading is appreciated
You can't add a class without specifying an element or a tag. If you want to add class name to the yield part, then wrapping it in a container is the only way.

Rendering views based on dynamic content in Ember.js Handlebars?

Is it possible to dynamically render content in ember directly from the template?
i.e., using 4 links that are bound to 4 different template names:
v1
v2
v3
v4
{{render view.view_to_render generic_controller}}
or are there more efficient ways to achieve this?
From the original post, I guess that you would like to render dynamically an actual view, rather than a partial template.
The snippet
{{render view.view_to_render generic_controller}}
does not work (in my experience), because ember tries to look for a view named 'view.view_to_render', rather than interpreting it as a variable to read the view from.
The solution I use is to have a custom helper:
Ember.Handlebars.registerBoundHelper( 'renderBoundView', function ( panel ) {
var args = Array.prototype.slice.call(arguments, 1)
Array.prototype.splice.call(args, 0,0,panel.view, 'panel.model')
// Call the render helper
return Ember.Handlebars.helpers.render.apply(this, args)
})
This helper extracts the view name from the variable 'view' in the passed object, and then passes that name to the standard render helper. Then using
{{renderBoundView panel}}
where panel has properties 'view' with the name of the view and 'model' containing the (resolved) model does the trick.
Of course you could also interpret the object passed as a variable name to get from the current context (which is also one of the arguments passed to the helper).
The entire purpose of Ember is to dynamically render content.
If you want to render particular "views" that are driven from data, that's pretty easy in Ember. Ember calls these partial templates "partials", appropriately-enough :)
Say you have an attribute called partialToRender set in your controller for the template that you're doing your "generic rendering" on. Say it's bound to a set of buttons which are bound to actions which each change the value of partialToRender. Something like this:
<button {{action changePartialToRender 'hello'}}>Change to Hello</button>
<button {{action changePartialToRender 'goodbye'}}>Change to Goodbye</button>
<button {{action changePartialToRender 'yes'}}>Change to Yes</button>
<button {{action changePartialToRender 'no'}}>Change to No</button>
and then in your controller you'd have an action something like this:
App.IndexController = Em.ObjectController.extend({
partialToRender: null,
actions: [
changePartialToRender: function(newValue) {
this.set('partialToRender', newValue);
}
]
});
That'd mean whenever the user clicked on one of your buttons, the value of partialToRender would be changing. Sweet, right? :)
So now all that we need to do is hook up our bit of template code that renders the partial. A partial is just another template, but it's part of a page rather than a full one... some bit of different content in each case, to render into our initial template...
So, we revisit the template like this:
<button {{action changePartialToRender 'hello'}}>Change to Hello</button>
<button {{action changePartialToRender 'goodbye'}}>Change to Goodbye</button>
<button {{action changePartialToRender 'yes'}}>Change to Yes</button>
<button {{action changePartialToRender 'no'}}>Change to No</button>
{{#if partialToRender}}
{{partial partialToRender}}
{{/if}}
Note I'm just wrapping the partial in an if statement to make sure it doesn't render if it's not set.
Also note that I haven't specified the partials here for you. I've just kind of whet your appetite. If you're really interested in this, I suggest watching the Ember video on the getting started guide in the ember site. It's a bit rambling, but it shows off some of Ember's powerful features, or possibly go through the guides / tutorial over at the Ember main site.
Hope that answers your question :)

How to get current Ember app route from template

I'm trying to set element class attributes based on current route. This is inside a navigation handlebars partial included in the Application handlebars template.
In my ApplicationController I can do something like:
isUsers: ( ->
#get('currentPath') == 'users.index'
).property('currentPath')
In the navigation template I want to do something like:
<a {{bind-attr class="isUsers:active:inactive"}} href="users"></a>
This doesn't work - inactive class is always set, even when in correct app path.
Any suggestions?
If you are defining the isUsers property in the ApplicationController, then are you sure you are using it in the application.handlebars or whatever template used by the ApplicationController? If you are using in a template which has a different Controller, make sure you use the following in your controller.
App.MyController = App.Controller.extend
needs: ['application']
In the template,
controllers.application.isUsers

how can i unbind an emberjs handlebar helper

I have a custom handlebar helper:
Handlebars.registerHelper('foo', function(key) {
return (key + ' bar');
});
and in my html I have:
{{foo beer}}
the result is
<div id="ember127" class="ember-view">beer bar</div>
how can I make my own handlebar helper act like the ember {{unbound beer}} and just produce "beer bar" without any additional markup ?
So I think you might be confused on how the helpers, templates, and Ember views work exactly. The markup you created is expected and is the exact markup you'd get with a working unbound helper.
Ember.Handlebars templates are always placed within an Ember view object (as you have above). Something that a normal bound helper would produce would be:
<div id="ember127" class="ember-view">
<script id="metamorph-1-start" type="text/x-placeholder"></script>
beer bar
<script id="metamorph-1-end" type="text/x-placeholder"></script>
</div>
Now if you want to surround your string with some other tag than a div (lets say an anchor tag or something), then you'd need to create a view, set it's template and tag name, then append that view.
Take a look at this jsFiddle and take a look at the results pane in your inspector for some examples of what I'm talking about. Hope that clears things up for you.
Ember has a helper called unbound that lets you wrap another helper. You can thus turn your bound (automatically) foo helper into an unbound one like so
{{unbound foo beer}}