React does not recognize the `hasCreate` prop on a DOM element - react-admin

Since migration to 4.2.7, all my list have an issue :
Warning: React does not recognize the hasCreate prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase hascreate instead. If you accidentally passed it from a parent component, remove it from the DOM element.
at div
at http://localhost:3000/static/js/bundle.js:15429:66
at Toolbar (http://localhost:3000/static/js/bundle.js:49221:82)
at http://localhost:3000/static/js/bundle.js:15429:66
at TopToolbar
at ListActions (http://localhost:3000/static/js/bundle.js:62975:25)
at div
at http://localhost:3000/static/js/bundle.js:15429:66
at Toolbar (http://localhost:3000/static/js/bundle.js:49221:82)
How to solve this?
thank you

in the most of the cases its happend when you add your custom prop to exist component so the DOM is doesn’t know what is it, so you can add $ like in this example:
const Component = styled.div`
font-size: ${({ $isMobile }) => ($isMobile ? '9px' : '16px')};
`
<Component $isMobile={true} />

Related

Insert scopedSlot into d3 svg using foreignObject

I'm creating a chart component using d3js and Vue (v2). In some parts, I want to support custom user content using scoped slots (in this case, custom tooltips)
<my-chart>
<template slot="tooltip" slot-scope="{ data }">
<span>{{ data }}</span>
</template>
</my-chart>
But I'm struggling to render this using d3js on a vuejs component render function. I'm trying to do something like:
g?.append("foreignObject").html(
this.$scopedSlots["tooltip"]({
event,
}),
);
Obviously, the html method isn't appropriated. I can't find anything like this online. All other examples use the component template to insert the foreignObject and Vue component on the SVG. Nothing using d3js
EDIT
As user I refer to developers. This code is for a lib.
Just in case anyone wants to implement something like this, I manage to resolve my issue.
Background
SVG has a concept of foreignObject which allows me to inject HTML inside an SVG. The next step is, somehow, to render the Vue component to HTML.
I'm using vue2, Vuetify, and d3js v6
Rendering the component
this.$scopedSlots["tooltip"]({
event,
}),
returns a VNode[], so using Vue.extend I create a new component, instantiate it and mount it to a div inside the foreignObject like this:
// Call the scoped slot, which returns a vnode[]
const vnodes = this.$scopedSlots["tooltip"]({
event,
});
const id = "RANDOM_GENERATED_ID";
// Append a foreignObject to an g
const fo = g?.append("foreignObject");
// Append a div to the foreignObject, which will be the vue component mount point
fo?.append("xhtml:div").attr("id", id);
// Create a new component which only render our vnodes inside a div
const component = Vue.extend({
render: (h) => {
return h(
"div",
{staticClass: "foreign-object-container"}
vnodes,
);
},
});
// Create a instance of this new component
const c = new component();
// I'm using vuetify, so I inject it here
c.$vuetify = this.$vuetify;
// Mount the component. This call will render the HTML
c.$mount("#" + id);
// Get the component rect
const bbox = c?.$el.getBoundingClientRect();
// Resize the ForeignObject to match our injected component size
return fo
?.attr("width", bbox?.width ?? 0)
.attr("height", bbox?.height ?? 0);
This successfully renders our component inside an SVG using d3js. At least it appears inside the DOM.
Problems
At this point, I faced 2 new problems.
Invalid component size
After rendering the Vue component inside the foreignObject it reported width equals 0. So, based on this I used the next styles:
.foreign-object-container {
display: inline-flex;
overflow: hidden;
}
And voilá, habemus a visible Vue component.
Scroll, ForeignObject, and the old Webkit Bug
My use case is this: The chart is responsive, so it re-renders after every container resizes (with some debounce), but to prevent deformations I set a minimum width to every element. With some screen sizes, this provokes some overflow, which inserts a scrollbar (browser behavior).
This is exactly what I want. But I'm using some Vuetify components on my scopedSlot which have a position: relative style.
Enters, an old bug on WebKit (Google Chrome, Safari, and Edge Chromium). This is better explained here and here
The solution in my case is simple. As I stated before, my foreignObject was resized to match the rendered component. So, to prevent my components to be wrongly drawn, I change my styles a little bit.
.foreign-object-container {
display: inline-flex;
overflow: hidden;
position: sticky;
position: -webkit-sticky;
}
Now, my teammates can use a generic chart with scroll support and customize some pieces of it using any Vue component (at least for now )

Get v-if expression/data as plain text in child component

Hy there,
I try to create a custom Vue component which is shown based on v-if directive. I also want to change the directive data (modalStatus) value from inside the component.
<modal v-if="modalStatus"></modal>
To update the data from the component i use a method similar to this.
closeModal () {
this.$parent.modalStatus = false
}
The problem is that sometimes i don't know the name of the data model (modalStatus) , can be anything.
My question is how can i get the data/expression name as a plain text from the modal component ?
I'm planing to use something like this to update the modalStatus
this.$parent['anyName'] = false
Thanks and stay safe !
Later Edit. I know how to accomplish all of the above using props or v-model. I wonder if is possible using strictly v-if. Thanks!
There are several approaches to get to a method or property in the parent component from the child.
The 'Vue Way' is to emit a message telling the parent to close.
Send the name in as a property
Parent
<child modalName='modalStatus' />
Child
this.$parent[this.modalName]=false
Send in a method
Parent
<child :close='onClose' />
// component method
onClose(){
this.modalStatus=false
}
Child
this.close()
Emit a message
Parent
<child-component #close='modalStatus=false' />
// or call a method
<child-component #close='onClose' />
// component method
onClose(){
this.modalStatus=false
}
Child
this.$emit('close')

Problem with creating a div into the document (DOM)

I have some problems with this fragment, it creates a div into the DOM so that div appears in every page as it is normal, which is an image.
How can I change it so this div is only being created when I access at this custom component (Model.vue) and no longer being visible once I'm out of the component page.
Thanks!!
container = document.createElement( 'div' );
document.body.appendChild( container );
If your HTML is predictable you should probably add it in your template (as HTML or as a component) and then show/hide it with v-if or v-show
If you need static content, for example depending on the user input, then v-html is the way.
If you need dynamic content (e.g. with event bindings), then check out render functions.
There's also dynamic component like <component :is="someComponent">, where someComponent can be either a string name of the component, or the component itself. But you should probably try the other solution first.
First of all direct DOM manipulation(Add/Del elements) is not at all preferred in vuejs. You can use v-if instead to show the element conditionally.
Or else if you still want to do direct manipulation then you can write the above lines of code in mounted hook of your model.vue component.
mounted() {
container = document.createElement('div');
container.setAttribute("id", "divId");
}
And then in before destroyed hook you can remove this element from the DOM as below.
beforeDestroy() {
var elem = document.querySelector('#divId');
elem.parentNode.removeChild(elem);
}

How to add multiple components to parent component in Vuejs

Let us assume we have 100 components. We usually add the component selector/name in HTML tags to the template of the parent component.But here we have 100's of components, so is there any dynamic way to add them??
There is a Vue tag:
<component :is="myComponent"></component>
Where myComponent is component name or whole component object. You can create array with component names and render them with v-for dynamically.
See Vue Docs for details about dynamic component.
Yes there is a way to dynamically add components.
In general there is three things.
1. instantiate the component
2. mount the component
3. add it to the dom tree
var ComponentClass = Vue.extend(Component)
var instance = new ComponentClass() //instantiate
instance.$mount() //mount
this.$refs.container.appendChild(instance.$el) //add to dom
Please don't do this. The <component :is> trick is handy to know, but this is a bit like getting a wedding invitation with the name of the bride on a sticky label - not convincing. You need to commit yourself sometime. The template with lots of of <component :is> tags will be impossible to understand and maintain.
This worked!!!!!
<div v-for="comp in components" :key="comp">
<component :is="comp"></component>
</div>

Accessing subcomponents via ref in vu 2.5.2 with typescript 2.4.2

I ran into a little problem trying to access subcomponents in vue 2.5.2 while using Typescript 2.4.2.
I am trying to access a Keen UI UiSelect component by adding it to $ref with the ref="selectfield" attribute and then access it in my Vue component with this.$refs.selectfield.
The UI-Select component is unrendered with the v-if="showSelect" directive.
onClickSpan(){
showSpan = false;
showSelect = true;
console.dir(this.$refs);
console.dir(this.$refs.selectfield);
}
I have referenced two different elements with ref.
I have already tried outputting this.$refs in the console and the Object selectfield is definitely there:
>{…}
|> currentNumField: <div style="width: 100%;">
|> selectfield: Object { _uid: 32, _isVue: true, "$options": {…}, … }
|> __proto__: Object { … }
undefined
Unfortunately trying to access it will always tell me that this.$refs.selectfield is undefined.
I am pretty new to Vue and Typescript and just don't know what could be causing this problem.
Thank you for your time.
You must give a little more information please.
Look at the example
https://jsfiddle.net/Jubels/50wL7mdz/78450/
this.$refs.paraG would return the node.
this.$refs.custom would return the vue component
<p ref="paraG">{{ message }}</p>
<custom-component ref="custom">{{ message }}</custom-component>
Hope this clear any uncertainty
I am not certain where the problem came from, but for everyone having similar issues here is a possible solution:
Use v-show instead of v-if.
It seems as though the problem is related to the way the DOM is rendered by using v-if.
The ref to the DOM Element is added to the $refs attribute of my component, but the variable $refs.mycomponent is empty since an element that is hidden with v-if is not only not rendered but not even added to the DOM.
I tried using a delay after making the object visible, but that did not solve the problem.
I can only assume that this is due to an issue with typescript that doesn't properly update the $refs attribute whenever the DOM is rerendered since this works with v-if when I am only using Vue.