I don't really know how to put it but i'll explain:
I have a form where the user can upload an image. The upload image goes as followes
<input id="visualisation_upload" #change="onThumbnailChanged" name="visualisation_upload" accept="image/*" type="file" class="sr-only">
onThumbnailChanged (event) {
const file = event.target.files[0];
this.fields.thumbnail = file;
this.fields.thumbnailPreview = URL.createObjectURL(file);
},
I want to show the user which files has been uploaded and let them delete the files if they so desire. For that exact purpose I use a component called uploadedFiles
I call the component like follows:
<uploadedFiles v-if="this.fields.thumbnail" :file="this.fields.thumbnail" :preview="this.fields.thumbnailPreview" #update-images="updateFiles"></uploadedFiles>
Within the uploadedFiles component I have a click Event that is supposed to empty both props within uploadedFiles AND the props that were passed to the component. When I try do to it directly within the component like this (Which you shouldn't do) I of-course get Avoid Mutating a Prop Directly:
deleteFiles() {
this.file = ''
this.preview = null
}
Within the parent component I have a method there I would like to empty the values like this
<a #click="$emit('update-images', file, preview), file = '', preview = null" class="hover:text-gray-900 cursor-pointer"></a>
updateFiles: function(file, preview) {
file = ''
preview = null
},
But I can't seem to make it work. I am trying to make the component dynamic so I don't have to create the method in each component.
I don't know if i am making any sense. Point is, when clicked on the button. Empty both this.fields.thumbnail & file from the component. How can I accomplish this?
You can emit update:xxx events and then use xxx.sync modifier in the parent (https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier):
<uploadedFiles
v-if="this.fields.thumbnail"
:file.sync="this.fields.thumbnail"
:preview.sync="this.fields.thumbnailPreview"
/>
deleteFiles()
{
this.$emit('update:file', '');
this.$emit('update:preview', null);
}
Related
I am in the beginning of learning Vue, and having a hard time understanding how to define props etc. using the composition API.
I have a component (ACE Editor), loaded like this:
<v-ace-editor
v-model:value="currentRecord.text"
/>
The v-ace-editor needs to have the model and value loaded like this: v-model:value.
import {computed, ref} from 'vue';
import collect from "collect.js"
const props = defineProps({
records: {
type: Object,
},
})
//Get all records.
let getRecords = () => {
return collect(props.records)
}
//Filter the records using "collect.js"
const getUnlabeledRecords = () => {
return getRecords()
.where('skipped', false)
.where('processed', false);
}
//Assign all unlabeled records on the first load.
let allRecords = ref(getUnlabeledRecords());
//In order to check if "text" is empty - and if not, set a default value, load the first record into a temp. variable.
let first = allRecords.value.first();
if(first){
first.text ??= "";
}
//Load the current record into a ref.
let current = ref(first);
//Finally, compute it.
let currentRecord = computed(() => current.value)
Looking at this, and coming from a backend background, it feels very bloated.
I have tried the following:
let allRecords = ref(getUnlabeledRecords());
let currentRecord = computed(() => allRecords.value.first())
But doing this leads to me not being able to interact with the currentRecord - nor change the allRecords. This means that if for example currentRecord.text is null from the backend, my ace-editor component fails because it expects a value.
Is there another way to load in these variables?
You actually don't have to called .value of a ref when using it in the template.
So you can actually remove the computed part (last line of your ) and change your template to.
<v-ace-editor
v-model="current.text"
/>
Now, assuming you managed v-model correctly in v-ace-editor (if this is your own component), you should have reactivity kept when modifiying current.text from v-ace-editor.
As a side note, computed properties are read-only. You cannot expect a child component to modify its value by passing it with v-model.
However, you should note that updating records prop from parent component will not update current. For this, maybe you want to add a watcher on records.
Also, personal suggestion, but if you only really care about currentRecord in your component and not all records, maybe you should do the filtering from parent component and only pass currentRecord as a prop. Other personal suggestion, you can declare all your variables in your script with const instead of let. const prevent reassignation, but since you work with refs, you never reassign it, but you change its value property.
I'm writing a component that uploads parameters to the server in promises. While the parameters are uploading I redirect to another component and send the parameters object as props, like this:
this.props.history.push('/new-page/createForm', {
parameters: this.state.parameters
});
At the create Form component, I'm receiving the props and rendering the state of the upload of the parameters, and they render as uploading (because they haven't finished uploading).
Back to the previous component, once the parameters upload promise finishes, I update the parameters object to reflect the new state, i.e, uploaded.
ParamsService.uploadParam(parameter, uploadRoot)
.then(() => {
parameter.status = 'Uploaded';
})
Now, once the parameters are uploaded, I want the create form to automatically re-render in order to reflect the new state of the parameters, but this is not happening automatically.
However, if I perform an action in the form, such as filling one of the input fields, which trigger a React rendering, the parameters show as uploaded.
Is there a way to recognize the props change in the new create form and re-render once the parameters state change to uploaded?
I've tried componentDidUpdate and getDerivedStateFromProps but such functions do not trigger when the parameters status change to uploaded.
Any help or guidance is appreciated.
see if react-url-query solve your problems.
as the example in readme, once you use the HOC like
export default addUrlProps({
urlPropsQueryConfig: {
foo: { type: UrlQueryParamTypes.number, queryParam: 'fooInUrl' }
},
})(MyComponent);
MyComponent will receive 2 props this.props.foo and this.props.onChangeFoo which allow you to get the value and allow you to change the url params ?fooInUrl
Solution for React hooks: use-query-params
I make an app with a nested component, the app call the component when i give an input, the input is the URL and that's the component property, now i can make the http request and i have my List, but when i change the input (URL) and re-clic nothing happen.
I tried to bind my property (URL), but didn't work.
I tried beforeUpdate() and after update(), same.. didn't work.
I tried this "" in my component, didn't work too.
I tried to destroy my component and create another one with the new value of URL, it works but it's not a solution..
data.svelte (component)
export let url;
function getData(){
const Http = new XMLHttpRequest();
Http.open("GET", url);
Http.send();
...
}
App.svelte
function newdata() {
Data = new data({
target: document.querySelector(".list"),
props: {
url: "",
}
});
}
onMount(async () => {
newdata();
});
function update() {
//data.$destroy();
url_input = document.querySelector("input").value;
Data.url = url_input;
//newdata();
}
App Html
<input type="url"/>
<button on:click={update}>Find</button>
<div class="list" />
I expect the component be reloaded when i change its property, but when i verify, url is changed but my component don't reload.
You don't need to do this for Svelte 3. The url from the input field can be passed as a prop to the Data component. When the input changes in the input field and the button is pressed, updating the prop will trigger the component to query for new data and update affected HTML elements.
I don't believe they have a v2 to v3 migration guide yet. It may be beneficial to go through the Tutorial so that you get a sense of what has changed.
Here's a REPL I created showing the process. I use jsonplaceholder to get a particular user (id range 1-10) and display the name, email and json data returned.
I'm having trouble determining if my component hierarchy really needs getDerivedStateFromProps, if the cases where it is needed is really as rare as the documentation makes it sound. It might be a fundamental misunderstanding about React/Redux design.
class AttributeList extends React.Component {
constructor(props){
super(props)
this.state = {
attributes: props.attributes,
newAttributes: []
}
}
addNewAttribute = () => {
// add new empty attribute to newAttributes state
}
onKeyChange = () => {
// update appropriate attribute key
}
onValueChange = () => {
// update appropriate attribute value
}
saveAttributes = () => {
// save the, API call
}
render = () => {
this.state.attributes.map((pair) => {
<Attribute
//pass data + functions, functional component />
})
this.state.newAttributes.map((pair) => {
<Attribute
//pass data + functions, functional component />
})
}
static getDerivedStateFromProps(){
// ?? do comparisons here to choose to remove or keep certain newAttributes? or just ignore result of save and keep interface as-is, just show error message if saving failed.
}
}
I have a parent component AttributeList which renders a bunch of Attributes, which are essentially key-value pairs. AttributeList receives the list of attributes of a document as props. However, the attributes can be edited, so it initializes its state (this.state.attributes) with this.props.attributes. Normally keys are immutable, but if a user adds a new attribute to the list, he can edit both the key and value. At any point, a user can save all the attributes. When the new attributes are saved, I'd like to disabled editing the keys for them as well. Here is the dilemma.
Option one is to save the document and just hope it worked, and then clear the new attributes list and mark all the attributes as saved (disabling the key input). I think this would be the "fully uncontrolled" solution, where once the state is initialized the component deals with everything on it's own. However, what if the save fails? I don't want to show and incorrect state to the user.
So I want to do option two. After save, fetch the document, which will load the attribute list and re-render the component. However I need to get rid of my new attributes since they are now a part of the attributes prop. I would like to verify that the new attributes are actually a part of the attributes prop now. It seems like this would happen ingetDerivedStateFromProps where I would on each render cycle check if any new attribute keys already exist in the attributes prop, and remove them from the "new" list if they do, and return that state.
But is this really the right time to use getDerivedStateFromProps? It seems to me that for any page that a user is "editing" something where you make an API call to save it, if you want to render based on the saved data ("the truth"), then I need to use getDerivedStateFromProps. Or perhaps from a design perspective it is better to show a message akin to "data not successfully saved" and keep the state as is, to prevent any data loss. I'm honestly not sure.
I don't see how getDerivedStateFromProps comes into it as there's no reason you need to copy props into state is there? When an old attribute value is changed you save it to the redux store, when new attribute properties are changed you can update local state (or save them to a different slice of the store, or differentiate them some other way). Update rules can be enforced in the update handlers or during merge on save.
// dispatch redux action to update store
onOldValueChange = () => {}
// this.setState to update new value
onNewKeyChange = () => {}
onNewValueChange = () => {}
render = () => {
this.props.attributes.map((pair) => {
<Attribute
//pass data + onOldValueChange, functional component />
})
this.state.newAttributes.map((pair) => {
<NewAttribute
//pass data + functions, functional component />
})
}
I create a SPA on Vue (something like a cloud disk), using the Element.ui tree component, to display the folder tree. The problem is that the tree itself does not load everything at once, but is loaded sequentially with the help of the lazy modifier.
The server part is mongoose + mongoose-path-tree. The use of the mongoose-path-tree means, that a tree storage scheme in the database is next: each node does not know about its children, but the children store their full path to the root element:
#root_id #subroot_id # ... #parent_id #this_id
The problem is that if the user makes changes to the tree (loads the file, creates a new folder, etc.) and the server accepts them, then how to notify the tree that it needs to load new data. Element ui in its documentation does not describe how to capture an event to cause the redrawing of a tree.
Client tree template
<el-tree
:props="defaultProps"
:load="loadNode"
#node-click="handleNodeClick"
:expand-on-click-node="false"
lazy
node-key="id"
ref="tree"
/>
Loading new nodes
loadNode: async function (node, resolve) {
try {
let firstGeneration = await foldersAPI.get(this.$store.state.user.myDrive);
if (node.level === 0) {
return resolve(firstGeneration.data.folder.children);
} else {
var data;
if (node.data.hasChildren) {
let children = await foldersAPI.get(node.data._id);
console.log(children);
data = children.data.folder.children;
} else {
data = [];
}
resolve(data);
}
} catch (e) {
console.log(e)
}
My solution was to simply rerender the treeview by adding the v-if attribute to the treeview and then creating a method
reload() {
this.show = false;
this.$nextTick(() => {
this.show = true
})
}
Calling that reload function then properly reloads the treeview
Try to pass computed variable as prop for tree component. All changes in computed will trigger rerender of tree.
you can set a key property to the component, and then, just change the key value, the component will re-render.
<el-tree
:props="defaultProps"
:load="loadNode"
#node-click="handleNodeClick"
:expand-on-click-node="false"
lazy
node-key="id"
ref="tree"
:key="myKey"
/>
read the doc for more details: https://v2.vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key