How to hold data without change in vue js - javascript

I have a user info object like this
data: function(){
return {
userinfo: {
name:'',
username:'',
imagePath:''
},
previousUserInfo:{}
}
}
and somewhere else i display this user infos:
<template>
<div> user name: {{userinfo.name}}</div>
<div> username: {{userinfo.username}}</div>
....
in some situation current user changes and i should save previous user data in previousUserInfo object:
this.$set(this, 'previousUserInfo', this.userinfo);
but when user fills new data in userinfo, previousUserInfo data also changes.
i also tried to user another variable to hold data but reuslts is the same :
var previousUserInfo = this.userinfo;
this.$set(this, 'previousUserInfo', previousUserInfo);
what's the problem?

this.userinfo and this.previousUserInfo are pointing to same object.
You can try doing deep copy value of this.userinfo
this.$set(this, 'previousUserInfo', JSON.parse(JSON.stringify(this.userinfo));

Related

Vue: Check form input is changed by user

I am new to Vue and want to achieve below result.
I have a form of data and a save button, before the page loads, it will fetch database and fill the form data. Because all the form data are filled, the save button is disabled and the user can not click unless the user change some data, then it knows the form data has changed, the save button will no longer be disabled and can be saved.
I know that should use watch property, but actually how I can implement this?
Thank you guys!
The form data is like this
data(){
return {
form: {
firstName: "",
lastName: ""
}
}
}
You can do something as below by having the two different objects actual and modified.
Here I have used underscore js for deep clone or isEqual, so don't forget to import.
computed: {
// Use is property for the save button to enable or disable
isDataChanged: function() {
return _.isEqual(this.actualForm, this.modifiedForm);
}
},
data() {
return {
actualForm: {
firstName: "",
lastName: ""
},
modifiedForm: {
firstName: "",
lastName: ""
}
}
},
methods: {
fetchData: function() {
// get data and assign it to actual form
this.actualForm = responseData;
this.modifiedForm = _.cloneDeep(this.actualForm) // this will create the new instance
}
}
You can use a watch on form like this below. You can also use deep:true if you need to watch nested property within form.
watch: {
form: {
deep: true,
handler(val) {
// Enable save button here. You can also evaluate any other condition to enable
}
}
}

Parse JSON Object from MongoDB GET

I have a ReactApp with a MongoDB based on local events here in my city. The current schema looks like this.
const eventSchema = mongoose.Schema({
start: String,
end: String,
name: String,
loc: { // Reference: https://mongoosejs.com/docs/geojson.html
type: {
type: String,
enum: ['Point']
},
coordinates: {
type: [Number]
}
},
web: String,
desc: String
});
I have an event button where I press, it gets the data from the DB and renders it to the HTML Page
callbackGetData() {
fetch('/api')
.then(resp => {
console.log(resp);
return resp.json();
})
.then(jdat => {
console.log(jdat);
this.setState({ data: JSON.stringify(jdat)}); // Print to debug area.
// todo: Update marker on the map?
});
}
when printed on the screen, this the results from the JSON objects that are stored in the database
[{"loc":{"coordinates":[-122.698687,45.526974],"type":"Point"},"_id":"5e5c3cde56138449ec49905f","start":".","end":".","name":"Portland Dining Month","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.680712,45.509871],"type":"Point"},"_id":"5e5c3cde56138449ec499060","start":".","end":".","name":"Test event #PSU","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.674043,45.481716],"type":"Point"},"_id":"5e5c3cde56138449ec499057","start":".","end":".","name":"Transgender Clients: Assessment and Planning for Gender-affirming Medical Procedures","web":".","desc":".","__v":0}]
If I try to go and print it with
console.log(jdat);
this.setState({ data: JSON.stringify(jdat.name)});
It says that it undefined. How can I go about printing out just the individual names and location coordinates and storing them in a variable I can use elsewhere in the file? Thanks
Looks like you have a array of data coming from database. so trying jdat.name will not work. You need to iterate over the array.
try the following code.Hope you are looking for the same
var jdat = [{"loc":{"coordinates":[-122.698687,45.526974],"type":"Point"},"_id":"5e5c3cde56138449ec49905f","start":".","end":".","name":"Portland Dining Month","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.680712,45.509871],"type":"Point"},"_id":"5e5c3cde56138449ec499060","start":".","end":".","name":"Test event #PSU","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.674043,45.481716],"type":"Point"},"_id":"5e5c3cde56138449ec499057","start":".","end":".","name":"Transgender Clients: Assessment and Planning for Gender-affirming Medical Procedures","web":".","desc":".","__v":0}]
jdat.forEach(item =>{
console.log("Name : ",item.name)
console.log("Location Coordinates : ",item.loc.coordinates)
})

Javascript, Redux thunk, synchronous / nested promises

I have a direct messaging application. All the data is stored in Firebase. Each chat contains an array of user IDs.
I use the following function to get all chats from componentDidMount():
return dispatch => new Promise(resolve => FirebaseRef.child('chats')
.on('value', snapshot => resolve(dispatch({
type: 'CHATS_REPLACE',
data: snapshot.val() || [],
})))).catch(e => console.log(e));
Which goes through:
chatReducer(state = initialState, action) {
case 'CHATS_REPLACE': {
let chats = [];
if (action.data && typeof action.data === 'object') {
chats = Object.values(action.data).map(item => ({
id: item.id,
title: item.title,
authorizedUsers: Object.values(item.authorizedUsers).map(user => ({
id: user.id,
// Somedata: fetchUserData(user.id)
// -> pretty sure it can't be done here <-
})),
}));
}
return {
...state,
error: null,
loading: false,
chats,
};
How would I go about fetching more data of every user inside each chat from Firebase at users/:uid?
I don't know what is the use case of this. It would be great if you can share, like how much information about the user you want to use. If its small data, why don't you add it in same API Only. You can pass the users data in the same object with user id as keys, and use the same keys inside your nested data like (only if user data is small or you know API data is always limited like because of pagination or page size. :
{
posts : [
{
title : 'abc'
authorizedUsers : ['1a', '2b', '3c']
}, ....
],
users : {
'1a' : {
name : 'john doe',
profileImage : 'https://some.sample.link',
},
'2b' : {
name : 'bob marshal',
profileImage : 'https://some.sample.link2',
}
}
}
If data is huge or cannot be added in the API ( because API is owned by 3rd party), then only place you can put you code is, instead of just dispatching the actions after the response is recieved, loop over the response in your service only, make async calls to get all "Unique users" only, append that data to the data you recieved from the previous api call, and then dispatch the action with the complete data to the store. It might not be the best way, as everything will have to stall i.e. even the data recieved in 1st api also will stall(not updated on screen) till all the users data is fetched. But best solution can only be given once we know more details about the use case. Like maybe lazy fetching the users data as end user scrolls the screen and may see a particular post Or fetching the user details once you start rendering your data from 1st API call like making a component for showing user associate with a post and in its componentDidMount, you pass the userIds as props from top component which might be "article/post/blog" component and it fetched the data at the time when it is actually rendering that "article/blog/post".
Hope this helps.

Dynamically passing JSON value to another template

I have one template, let's call it Template A that prints JSON data into a table, one column includes a button which is conditionally rendered when has_violations equals true.
An example of the table:
Table
What I want to accomplish is to take the driver_id that is associated with that particular row into the router link and have it passed onto a different template file let's call it Template B.
But how can I accomplish this using Vuex Store?
Sample JSON data:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Basic steps:
Get index of row on button click.
Get index of JSON data using value from Step 1.
Store the JSON data from Step 2 into Vuex.
Send user to Template B using router.
Retrieve data from Store when in Template B
Because you did not show your exact structure, the code below is just a basic structure.
Here's the code:
/* VUEX demo */
new Vuex.Store({
state: {
driver_data: undefined
},
mutations: {
recordDriver({ state }, payload){
state.driver_data = payload;
}
}
});
/* TEMPLATE A demo */
new Vue.component('template-a', {
data: function(){
return {
// Assume this is the JSON
driverJSON: [
{ driver_id: 1, driver_name: 'John Smith' },
{ driver_id: 2, driver_name: 'Bob John' }
]
};
},
methods: {
onButtonClicked: function(e){
const button = e.target;
const td = button.parentElement;
const tr = td.parentElement;
const indexOfTr = [...tr.parentElement.children].findIndex(row => row === tr);
const dataToStore = this.driverJSON[indexOfTr];
// Store data into $store
this.$store.commit('recordDriver', dataToStore);
// After storing, direct page using $router
this.$router.go({ ... });
}
}
});
/* TEMPLATE B demo */
new Vue.component('template-b', {
data: function(){
return {
// Get driver data using $store
driver: this.$store.state.driver_data
}
}
});
I like Yong's answer, but I would rather suggest you to pass the driverID as a prop to your route and then use a VueX getter to get the violations for the particular ID.

How to add additional fields to user documents (not in profile)?

I know that the classic way to add data to user collection is in profile array, but according to this document, it is not the best way to store data.
Is there an alternative to that, for example to create a field in the root of user collection at the same level with default fields (_id, username, etc.)?
There is nothing wrong per-se with the profile field, other than the fact that a users can (currently) directly update their own profile by default.
I don't find this behavior desired, as a user could store arbitrary data in the profile.
This may become a real security risk if the developer uses that field as a source of authority; for example, stores the user's groups or roles in it.
In this case, users could set their own permissions and roles.
This is caused by this code:
users.allow({
// clients can modify the profile field of their own document, and
// nothing else.
update: function (userId, user, fields, modifier) {
// make sure it is our record
if (user._id !== userId)
return false;
// user can only modify the 'profile' field. sets to multiple
// sub-keys (eg profile.foo and profile.bar) are merged into entry
// in the fields list.
if (fields.length !== 1 || fields[0] !== 'profile')
return false;
return true;
}
});
The first thing to do is to restrict writes to it:
Meteor.users.deny({
update() {
return true;
}
});
It could then be updated using methods and other authorized code.
If you add your own fields and want to publish them to the currently logged-in user, you can do so by using an automatic publication:
Meteor.publish(null, function () {
if (this.userId) {
return Meteor.users.find({
_id: this.userId
}, {
fields: {
yourCustomField1: 1,
yourCustomField2: 1
}
});
} else {
return this.ready();
}
});
Meteor.users is just a normal Mongo.Collection, so modifying it is done just like any other Collection. There is also the creation hook, Accounts.onCreateUser which allows you to add custom data to the user object when it is first created, as mentioned in #MatthiasEckhart's answer.
You could add extra fields to user documents via the accountsServer.onCreateUser(func) function.
For example:
if (Meteor.isServer) {
Accounts.onCreateUser(function(options, user) {
_.extend(user, {
myValue: "value",
myArray: [],
myObject: {
key: "value"
}
});
});
}
Please note: By default, the following Meteor.users fields are published to the client username, emails and profile. As a consequence, you need to publish any additional fields.
For instance:
if (Meteor.isServer) {
Meteor.publish("user", function() {
if (this.userId) return Meteor.users.find({
_id: this.userId
}, {
fields: {
'myValue': 1,
'myArray': 1,
'myObject': 1
}
});
else this.ready();
});
}
if (Meteor.isClient) {
Meteor.subscribe("user");
}

Categories