I like to think of Angular components as element directives. Given a component called hero I can use it in the parent template like this:
<hero myparam="something"></hero>
I'd like to use the hero element as a component-managed container, with the component being in charge of the whole element.
EXPECTED
Here's what I hope to get out of the binding from above:
<hero id="component123" class="alien" custom="foo">text</hero>
My custom component transforms the given element and uses it as it sees fit.
ACTUAL
However, it seems the component can only render its template inside the hero element. Best I can get is:
<hero myparam="something">
<div id="component123" class="alien" custom="foo">
text
</div>
</hero>
I feel this is bad because the hero element is not actually the hero, but merly a wrapper for the actual hero. This mixes up the semantics and creates unwanted extra elements.
Is it best practice in Angular to use components as pure wrappers and putting the actual components inside?
Here's an official sample to play around with:
https://plnkr.co/edit/NKjCDS8OEngYHNrBmC5O?p=preview
I like to think of Angular components as element directives.
A component could be thought of as a special kind of directive that updates a template. If you want to change attributes, use an actual directive (attribute directive)
My custom component transforms the given element and uses it as it
sees fit.
When you talk about transforming, you want directives, not components.
Related
I'm learning Vue.js. Here I want to design a checkbox component like this:
<xxx-checkbox-group>
<xxx-checkbox></xx-checkbox>
<xxx-checkbox></xx-checkbox>
</xxx-checkbox-group>
Above are two kinds, the parent checkbox-group and the child checkbox. The checkbox-group use <slot> to get the children component and have some necessary data. I want the child can use some parent's data(just use not change). It's a bit troublesome and inefficient if I put a attribute in every child's tag.
<xxx-checkbox-group>
<xxx-checkbox shape="square"></xx-checkbox>
<xxx-checkbox shape="square"></xx-checkbox>
</xxx-checkbox-group>
Can I pass the value by other ways? So that I can controll all the children's state through the parent. I think it's possible but I can't find the way to achieve that.
<xxx-checkbox-group shape="square">
<xxx-checkbox></xx-checkbox>
<xxx-checkbox></xx-checkbox>
</xxx-checkbox-group>
For me it's not fully clear what's your intention, so can you create a small jsfiddle example?
but in general, within a child component you can access to it's parent vue instance via this.$parent to read & change properties.
In Vue.js, I have a var app = new Vue({...}); and I have a component Vue.component('mycomponent', ... where I can use such component without any issue by directly adding <mycomponent></mycomponent> in html. What I wish to do is to dynamically add those component on demand maybe after a button click or when some other such event takes place. In raw JS, I'd use document.createElement... when event fires and then do el.appendChild.. to add it into html. How would I do the same with Vue.js ?
I'm not doing anything fancy with node js. This is on a single html page with <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> in the <head>.
To do this the "Vue way" usually involves the use of v-if, v-for or <component>, depending on what you want to do:
Use v-if to conditionally show a component/element based on a condition.
Use v-for to render a list of components/elements.
Use <component> to render a dynamic component/element.
So to achieve what you described in your question, you can declare a boolean data property visible like this:
data() {
return {
visible: false
}
}
and use it with v-if to control the visibility of a component in the template:
<mycomponent v-if="visible"></mycomponent>
This requires that <mycomponent> exist in the template upfront. If you don't know what kind of component you want to show, then you can either include each possibility in the template and display the one you want based on some condition:
<comp1 v-if="comp1Visible"></comp1>
<comp2 v-if="comp2Visible"></comp2>
<comp3 v-if="comp3Visible"></comp3>
or you can use <component> together with another data property (comp) which you can set to the name of the component you want to display:
<component v-if="visible" :is="comp"></component>
What you described (document.createElement followed by el.appendChild) does not exist in Vue. Vue has a strict rendering mechanism which you need to work with; it isn't possible to dynamically instantiate components and stick them into the DOM randomly. Technically you can do comp = new Vue() as an equivalent to document.createElement and then el.appendChild(comp.$el), but that probably isn't what you want to do because you would be creating an independent Vue instance that you would have to manage manually with no easy way of passing data around.
In angular we could do:
template: `<ng-container #c></ng-container>`
or:
template: `<template #c></template>`
To create a view container that is hidden when the template is rendered.
Is there a difference between using ng-container over the html template alternative? I'm guessing Angular has to have it's own template containers like ng-template or ng-container since use of the raw html template element could break non browser based runtimes, like mobile clients, etc.
The <ng-container> is always rendered, but does not represent a DOM element. It is still attached to the component's view.
The <ng-template> will only be rendered if it is explicitly requested.
Here's a good reference on the subject:
http://nataliesmith.ca/blog/2018-05-01-ngtemplate-ngcontainer-ngcontent/
To create a view container that is hidden when the template is rendered.
Always use <ng-template> when possible. The <ng-container> is for grouping DOM elements together. For example; when you need to apply a *ngIf to many <td> elements, because you can not use a <span> around <td>.
I am importing SVG elements into React component, and then rendering them as components.
How do I attach a reference to React Components that contain SVG elements from within my componentDidMount()?
Well, i don't see code example. U can try to use jQuery, when your component render will finish - this is magic stick in many situations. Just type smth like:
let element = $('#element_id');
And you will get access to its information
I have a set of components app->page->list_container->list->item
My goal is to notify the app that click happened on item level.
If there is simple relation like parent->child I could use props and do something like: <Child onClick={this._onClick}> ... and then use this.props.onClick() to make a callback.
But what is the best native React-style receipt for doing the same trick with a tree of components? How to notify the app, that item was clicked without calling to Flux/Reflux, Elm and other supported libs?
Standard react way:
Passing onClick function as a prop down your component tree is the standard react-way of doing this.
In <app>:
<page onClick={this._onClick}>
In <page>:
<list_container onClick={this.props.onClick}>
Etcetera.
Or you could use:
<list_container {...this.props}>
To automatically pass down any prop from parent component to the child component.
In a deep tree, this can and will get quite tedious/ lot of work. React was not designed for this purpose.
React is made for (top-down) smart and fast component-tree rendering.
The frameworks for the flux pattern you mention are designed to handle the other interactive functions.
Alternative shortcut (not recommended):
A shortcut you could possibly use is to add a listener directly on the DOM, inside your <app> component, that handles the click event on the item:
In <app> component, add:
componentDidMount: function() {
var itemElementInDOM = document.getElementById('myItem');
itemElementInDOM.addEventListener('click',this._onClick);
}
And in your <item> component, give the item a (unique) id.
I would generally NOT recommend this:
In a typical react tree setup, the lower level components (like
<item>) may be rendered more than once, and then you would need
additional logic to ensure that each ID is unique.
You would also
need to add some additional smarts to make sure you remove the listener if the
item(s) in question are removed from the DOM.