Mithril.js: Should two child components talk to each other through their parent's controller? - mithril.js

I'm a bit stuck looking for the right way to do the following. I have a parent component with two child components (see simplified code below). I would like to know the proper way for ChildA, when its button is pressed, to 'invoke' ChildB. They should communicate through the parent, but should they communicate through their controllers? In that case the parent has to pass its controller to the controllers of the children.
Parent = {
view: function () {
m.component(ChildA);
m.component(ChildB);
}
}
ChildA = {
view: function () {
m('button')
}
}
ChildB = {
view: function () {
}
}

This is really just a question of the plain javascript style you prefer. Think of communication between JS objects and how you would handle that.
Generally, Mithril devs choose between Parent/Child communication and Pub/Sub. For Parent/Children, the controller is commonly where devs place their logic. m.component accepts multiple parameters in which you can pass references (data/state/logic) to child components. See the docs. There's no need to pass Parent controllers to children.
However, I prefer to create a view-model which lives outside of any one component. It's where I keep view state (ie. form data) and view logic (ie. events, UI related callbacks, and shared state between components). This way, when I inevitably change/add components, I will not have to rework the controller logic in each component.
Leo Horie, the author of Mithril, wrote an article in which he explains communication between a Parent and One Child, but this can effortlessly be applied to multiple children.
Pub/Sub is a common JS idiom. The Mithril wiki lists a few community contributions that handle this. Go to the wiki, open the page titled "All Topics" and do a normal page search for "pub". You'll discover a few options there. Depending on the complexity of your app, your next move might be to search for a Pub/Sub library through google.

I guess when you press the button of ChildA, some model state will be updated? Then, if both childs share the same model, the redraw done by Mithril after each event will update ChildB automatically.
My suggestion is to pass the model to the child objects and let ChildA also be a controller for the button. Why not the parent? Parents should usually handle commands that apply to multiple child views (or itself), and a button click seems simple/coupled enough for ChildA to manage that itself. But it depends on the real complexity of the system, of course. Always the problem with a simple example, it does not describe reality. :)
Here's an example how I mean anyway: http://jsbin.com/sipahe/edit?js,output
(Sorry for the Coffeescript, but it's brief and conveys the meaning very well.)

Related

Can a Parent and Child listen to same event and merge data together - Vue?

If I have a button in the parent class, and a child class stores certain data. The parent class also stores different data.
When we click on the parent's button, is it possible for the child class to listen to this event and then pass this data into the parent, then the parent also grabs its own data and merge this data together?
How can they both listen to the same button event, and then retrieve data and pass data to one another? The data needs to be passed through another function to format/change the values too, not just displaying this data.
Is this possible and how, since this button seems to affect two components and data is passed along?
Thanks!
Children cannot catch parents' events. Children can expose methods that parents can call, or you can create a reactive property that represents whatever state changes when the parent button is clicked.
Events are messy and low level and you shouldn't mourn not being able to use them. Using state and reactivity is much cleaner. Vue components should really only store "private" state. Props are available for passing state down to immediate children. It's much better to extract state into a) a plain old javascript object, passed around via data or provide/inject, or b) vuex, then watch reactivity work its magic.
When you define your data, you can reference anything anywhere. So if you create an object in global scope, let's call it $myState...
Vue.prototype.$myState = { myProp: "hello", myName: "Simon"}
then you can reference it in your data in any component...
data : function(){return { myState : this.$myState }
Some people will object that doing this creates spaghetti - anything anywhere can modify your global state object, but for many many applications, the simplicity gained by "normalizing" state and storing it centrally will outweigh the costs.

Vuejs have multiple components sharing variables and functions

Right this is more about practice, I haven't really got a working scenario but I can provide an example. Imagine Facebook messenger, you have a chat widget in the header where you can quickly view and respond to messages. You can also click view all to take you off to the messages page.
So to me I can see myself having two Vue components, they're more sibling than parent and child as one would be used on all pages and the other simply on the messages page.
<chat></chat> and <chat-widget></chat-widget>
Now from what I can see, across the widget and the chat window itself is functions that would operate in the same way, maybe they'll have slightly different templates because of where they are loaded but somethings straight of the bat would be:
messages() {}
compose() {}
validate() {}
These are just some examples as I use Laravel as my backend I would be using axios to send requests between my Vue frontend and my backend (database).
Ideally, I wouldn't want these components to duplicate it's functions, so should I simply duplicate them or is there a way where i can store some sort of parent functions?
One problem is because of async ajax requests I can't simply call a function that returns say the messages for me to bind, at least, I don't think I can.
Just looking for some guidance on how I can best do this with Vue so that I'm not duplicating identical functionality within my components.
You can use composition to create a "base/abstract" component from which other components can extend:
https://v2.vuejs.org/v2/api/#extends
var CompA = { ... }
// extend CompA without having to call `Vue.extend` on either
var CompB = {
extends: CompA,
...
}
You can inherit the functionality from the "base/abstract" class or override parts of the functionality of the parent.
Another thing you could do is to create mixins:
https://v2.vuejs.org/v2/guide/mixins.html#ad
This is helpful if your components need to share functionality but are not largely the same. If you were to use extend in those cases you would likely override the majority of base functionality to these are a better in that case.
Of course nothing would stop you from simply extracting commonly used methods into a separate file and importing them in your components directly.

Vue common component communicate with different parent

I have a requestion, how about vue.js's common component's best practice when communicating with different parent component.
for example, in my scenario, a basic modal component, trigger a 'close' method, but it has two different parent component
I find two solutions:
parent need pass an additional prop, and then baisc component just
trigger event which event's name is the prop value, so the listener
parent component attched on could be called
in basic modal just use this.$parent to visit parent component methods, or this.$parent.trigger('xxx'), and then parent knows what to do
But, both above I think not very good, the first may need pass an additional prop, this let others who write a third, a forth parent component use the basic component not very handy. And the second may be felt more hard coded.
So, is there is better solution in this case?
Use this.$dispatch('eventName', data) (for Vue 2.x, use this.$emit('eventName', data)), to trigger an event to any parent, grandparent and further up the chain (you can use this.$broadcast('eventName', data) to trigger events down the chain in Vue < 2.x).
If the parent has an event called 'eventName' then it will fire this event.
If you have multiple parents, you can give them each a different event and from the child fire this specific event via dispatch. You can also give each parent the same event and pass a data prop that specifies what the parent should do. Third option is to refer to the specific parent:
var parent = new Vue({ el: '#parent' })
// access child component instance
parent.$refs.eventName()
Each option has pros and cons. The best solution depends on the context. But I think that the best solution in general is option 1. Then you don't need an additional data parameter. Option 3 is not loosely coupled.
For more info: https://vuejs.org/guide/components.html

The idea of smart and dumb components in Elm

I like React's/Redux' concept of smart and dumb components, where a dumb component does not handle its own state (Dump component does not know anything about the outside world, all it does is triggering an event and displaying value according to its input). This is trivial because all state in handled in one place (main reducer).
In Elm, each component has its own 'update' function (similar to Redux' reducer), so it does not seem trivial to use the same (dumb & smart components pattern).
Is using smart & dump components a good practice in Elm? If so, will I have components without 'update' method? I wonder how will I pass data (props) to my component and how will I trigger events to the parent component.
I will love to hear your thoughts.
The Elm equivalent of a "dumb component" (a.k.a. Presentational, Pure, Skinny component) is simply a function that produces Html:
view : ... -> Html
The elm-html library is written in this style, e.g.,
button : List Attribute -> List Html -> Html
You set the "props" by providing attributes when you call the function. In particular, you register events by supplying handlers in List Attribute:
button
[ onClick addr ClickAction ] -- event handler
[ text "Click me" ] -- child "components"
You'll see this pattern also in other libraries, although the exact types may be different from List Attribute and List Html.
Another Smart/Dumb distinction you can make is between components that return Effects and those that don't. But to answer your question...
Nothing stops you defining actions in a child
type Action
= Submit
| Click
and in the Parent view having
Child.view (Signal.forwardTo address ChildAction) props
(We pass props because there is no model data as such to pass)
but then handling all the actions in the Parent's update:
case action of
ChildAction act ->
case act of
Child.Submit ->
...
Child.Click ->
...
This would be essential anyway if the impact of an Action in Child was to change the state in Parent or some other child of Parent.

Attach/Render RactiveJS component outside of template

I've got an existing SPA that was developed using nested RactiveJS components. Which is great, and offers a ton of flexibility throughout the entire app. Currently I attempting to add in client side routing support using page. My navigation switches out high-level components using simple {{#visible}}{{/visible}} template markup on each component. This is a little troublesome in its current state as it always kicks off a re-render whenever the high-level component becomes visible again.
Is there a way to render a component, for example, called widget, without using the
<widget></widget>
method? I've already "registered" the component with the parent, but obviously when constructing it by means of
new App.components.widget
I am able to control how/when it's rendered/inserted/detached, but lose the recognition in the application's component hierarchy.
There is insert exactly for that. You don't even need to "register" it to the component you plan to put it to. You can use the different find* methods or nodes to easily retrieve a reference of your planned container element.
var instance = new YourDetachedWidget({ ... });
instance.insert('#your-container'); // This could be a node, selector or jQuery object