Ember communicate between child components - ember.js

I'm trying to make a header component, which has several inside components,
such as a button, and one of them opens a side menu that is a child component also.
How can target an action of one child component to another child component?
In this case, target the open action in the button component to the overlay component?
{{#header-block class="main-nav"}}
{{button-icon icon="fi-torso" class="nav-action left" openMenu=(action "open" target=) }}
{{#overlay-block}}
{{#side-menu}}
<p>side menu one</p>
{{/side-menu}}
{{/overlay-block}}
{{/header-block}}

You can't, at least not using the public API. You may have read this before, but Ember's components follow the "data down, actions up" approach. In order to do what you want, you have to send an action from the button component to your controller, mutate some data based on that action, then pass that data into the overlay component. There can be no direct communication between the two components.

Related

How do you test complex parent child communications using testing library

I would like to understand how to test a parent component that depends on the complex logic in one child and updates another one.
Let's imagine we have a huge form (parent) on a page and we want to have the ability to update it.
When there is at least one change we should show a confirmation bar (a child) to apply the change.
To amend the form data we have an option to click on a button to open a modal component (a child) with a form group inside. Moreover, the modal makes an API call to fetch some data and uses a timeout to apply new changes.
What would you test in the case?
P.S. It's easy to test each child component separately but when you try to write a functional test for the parent component then you may notice that the test is complex and unmaintainable due to async login in children

Set focus on component after modal close

I would like to know what is recommended way to focus on component from outside the component. I am trying to focus on a input field when the person closes the modal window.
Typically you would use an action in the modal that triggers when the close button is clicked, and you would send the action up to the component with the input with sendAction. In the component with the input, you could then use Ember.$ or any other method to tell the browser to focus on an element.
The situation is more complex if the modal is not inside the component that contains the input. In that case you might need to use a service or event. But it's much easier to use the first method.

Bind a class to dynamic service property

I have a service which I call state that just handles loading states and I can expand it to whatever I may want in the future. I needed this service so I could have different components talk to each other in a way so they would know if another component is loading or doing something.
Anyway I have this button that I use in different places that just sends an action, and then turns into a spinner until that action is complete.
They way the loading animation works currently is classNameBindings: ['state.working'],
However if I now have two of these buttons on screen at the same time, and I call this.state.set('working',true); all the buttons are now spinning.
I would rather pass in a property name to the button component that tells it what property to watch on the state service to determine if it should add the working class or not
I'm just having some trouble figuring out how to make this work in the component.
How can I have the class binding watch for a dynamic property name that will be passed to the component as something like loadingPropertyName so each button component can watch a different property for it's working class binding.
You can do something like this:
init() {
this.set('classNameBindings', 'state.'+this.get('stateProp'));
this._super();
}

Ember: Call a component action from a controller

I have a component working pretty well and now I need to call it inside a controller.
Scenario: I have an ember application, and I have an update button controller, I did a component that just display a toast(Materializecss) with some message I pass as parameter to the component and both the button and the toast are working well separately. I need to call inside the button controller this component to display to the user if the update was successfully or not using this component I did. Any sugestion of how can I call this component inside the controller? Thanks
have a look at the ember-twiddle I created and see if it fits the bill, in regards to what you want to do ?
You should instead of thinking "calling the component", rather, how can I push updated attributes/data to the component.
Ember relies on the "Data Dow Actions Up" pattern. This implies that you cannot make an explicit call to a component action from a controller. (see https://dockyard.com/blog/2015/10/14/best-practices-data-down-actions-up for example)
Instead, a better design should be to define a service to manage data : messages to be "toasted". Then make this service available by injecting in in your controller. You will be able to call methods to register a new messages and generate new data.
Provide also a component template (to be included in your own templates) that will be in charge to display the new message, etc. Each change in the data managed by the service will lead to a component template update.
You should definitely take a look to https://www.npmjs.com/package/ember-toastr

Ember.js - how do I bind a view to a state manager, and ensure that view is rendered?

I am using a StateManager to control the state of a pop-up modal (e.g., the states are "open.edit", "open.show" and "closed"). I'd like to use a state manager here because the modal is quiet complex and requires it's own transaction (I'm using Ember data).
I am able to set the appropriate data, view and controller on my ModalStateManager.
However, the view (in this case App.ModalView) is never rendered in the DOM. I know this because I've put logging statements in didInsertElement function of my App.ModalView, and those never get logged.
How can I render the view when someone clicks the button to open the modal?
Here's the code that is run on my ModalStateManager when someone clicks to open the modal.
App.ModalStateManager = Ember.State.create({
closed: Ember.State.create({
open: function(manager, modalData) {
var view = App.router.get('applicationController').connectOutlet("modal", modalData);
//this is working
//the view returned is the ModalView; it has a ModalController with expected content
manager.transitionTo('open.show');
}
})
//omitting other states for simplicity
)}
Larger question: How should you build a view that has multiple states and dynamic data, but does not have its own url or state within the router? e.g., Imagine a page with a list of unique items. Clicking an item pops open a modal that shows the item content, allowing the user to edit and save it. The modal doesn't have its own url or state in the router, so its not as easy as setting a dynamic state /:item_id in the router that can be easily wired and updated.
In one of our apps, we have a PanelManager (subclass of StateManager) that handles state for our modals. There is also a PanelController, which has properties that our panel container view binds to for className and visibility. When transitioning from closed to a particular open state (e.g. showingEditPanel), the manager sets the classname and visibility properties and calls connectOutlet on the panelController to show the correct view/controller combo within the panel container. Additional complexity can be modeled with nested states under each open state.