UPDATE
If I change my delete function to this:
function deleteResNote(reservation, note_idx) {
realm.write(() => {
reservation.notes.splice(note_idx, 1)
})
}
Everything works fine. No errors, and the UI updates accordingly.
Why do I need to delete my notes through my reservation object? Why doesn't realm allow me to delete my Notes objects directly?
My app deals with Tasks, which have Reservations, which have Notes. I'm working on the ability to delete a note.
My notes render in a ListView. When the cell is tapped, I pass the Note object (Realm Object) through to my deletion method, and ultimately call:
function deleteResNote(noteObj) {
realm.write(() => {
realm.delete(noteObj)
})
}
After this delete, this method triggers the following error:
Error: Accessing object of type Notes which has been deleted
The component which holds my ListView is set up to listen for the realm change event, and the same method used to render for the first time is used again to re-render the updated notes. I think it's important to point out that in this method, Notes are accessed through the parent Task object.
handleReservationNotes = taskId => {
let dataBlob = {}
let sectionIDs = []
let rowIDs = []
let reservations = []
const details = Tasks.getTask(taskId)
Object.values(details.reservations).forEach((reservation, i) => {
reservations.push({ id: reservation.id, lead: reservation.lead_name })
// manage our sections
sectionIDs.push(reservation.id)
dataBlob[reservation.id] = reservation.lead_name
// manage our rows
rowIDs[i] = []
Object.values(reservation.notes).forEach((note, j) => {
rowIDs[i].push(j)
dataBlob[`${reservation.id}:${j}`] = note
})
})
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs),
reservations,
selectedReservationId:reservations[0].id,
})
}
It seems as though I'm properly deleting the note from the database, but my Reservation object seems to be still holding on to it.
How can I successfully delete a Note while updating all the appropriate relationships?
Related
I'm trying to build a simple app that lets the user type a name of a movie in a search bar, and get a list of all the movies related to that name (from an external public API).
I have a problem with the actual state updating.
If a user will type "Star", the list will show just movies with "Sta". So if the user would like to see the actual list of "Star" movies, he'd need to type "Star " (with an extra char to update the previous state).
In other words, the search query is one char behind the State.
How should it be written in React Native?
state = {
query: "",
data: []
};
searchUpdate = e => {
let query = this.state.query;
this.setState({ query: e }, () => {
if (query.length > 2) {
this.searchQuery(query.toLowerCase());
}
});
};
searchQuery = async query => {
try {
const get = await fetch(`${API.URL}/?s=${query}&${API.KEY}`);
const get2 = await get.json();
const data = get2.Search; // .Search is to get the actual array from the json
this.setState({ data });
} catch (err) {
console.log(err);
}
};
You don't have to rely on state for the query, just get the value from the event in the change handler
searchUpdate = e => {
if(e.target.value.length > 2) {
this.searchQuery(e.target.value)
}
};
You could keep state updated as well if you need to in order to maintain the value of the input correctly, but you don't need it for the search.
However, to answer what you're problem is, you are getting the value of state.query from the previous state. The first line of your searchUpdate function is getting the value of your query from the current state, which doesn't yet contain the updated value that triggered the searchUpdate function.
I don't prefer to send api call every change of letters. You should send API just when user stop typing and this can achieved by debounce function from lodash
debounce-lodash
this is the best practise and best for user and server instead of sending 10 requests in long phases
the next thing You get the value from previous state you should do API call after changing state as
const changeStateQuery = query => {
this.setState({query}, () => {
//call api call after already changing state
})
}
Not sure where to start with this one... I'm creating a basic todo app, using localStorage. (I specially, am not using a backend database).
So far, I can update and display, the objects I have stored locally. And I am displaying them on my page.
form.addEventListener('submit', function(e){
e.preventDefault();
// Set object
let data = {
name: nameInput.value,
url: urlInput.value
};
bookMarksArray.push(data);
console.log("Added bookmark #" + data);
// Saving
localStorage.setItem("bookMarksArray", JSON.stringify(bookMarksArray));
});
However, I also want to able to edit, each item in my DOM. Once edited, I want that specific object, which correlates to this, to be updated in localStorage.
How do I do this?
I'm not sure where to start. Here's a codepen, of my code so far:
https://codepen.io/ReenaVerma1981/pen/LYEPbjL
EG
- if I want to update a URL value to www.google.co.uk
- And this is updated, in the correct object, in localStorage
Here's some psuedo code, is this a good approach?
// List each object as an individual form in DOM
// So I can easily update the input.value, (with a new value)
// The **edit** button, would be a submit button
// Or there's an eventHandler on this button
// Which onClick, takes the input.value, (either name.value or url.value)
// Identifies whether these values, match values in the localStorage object
// And if so, get the object index
// Then update these object values, based on the object index?
// Update localStorage via localStorage.setItem
Here's some example code, I'm writing, to try and do this:
// UPDATE/EDIT EXISTING
const list = document.querySelector('.url-list');
list.addEventListener('click', event => {
if (event.target.classList.contains('js-edit-url')) {
console.log('edit');
const editName = event.target.parentElement.name.value;
const editURL = event.target.parentElement.url.value;
let data = {
name: editName,
url: editURL
};
Object.keys(bookMarksArray).map(function (old_key, index) {
// console.log('old_key',old_key);
let new_key = data;
console.log('data', data);
if (old_key !== new_key) {
Object.defineProperty(bookMarksArray, new_key,
Object.getOwnPropertyDescriptor(bookMarksArray, old_key));
// console.log('bookMarksArray',bookMarksArray);
// localStorage.setItem("bookMarksArray", JSON.stringify(bookMarksArray));
delete bookMarksArray[old_key];
}
});
}
});
I figured it out!
I found a really good example here, using the ES6 way, without mutating original data:
// UPDATE/EDIT EXISTING
const list = document.querySelector('.url-list');
list.addEventListener('click', event => {
if (event.target.classList.contains('js-edit-url')) {
console.log('edit');
const editName = event.target.parentElement.name.value;
const editURL = event.target.parentElement.url.value;
// Find the object index, by looping through and matching name.value
const objIndex = bookMarksArray.findIndex(obj => obj.name === editName);
// make new object of updated object.
const updatedObj = { ...bookMarksArray[objIndex], url: editURL};
// make final new array of objects by combining updated object.
const updatedProjects = [
...bookMarksArray.slice(0, objIndex),
updatedObj,
...bookMarksArray.slice(objIndex + 1),
];
localStorage.setItem("bookMarksArray", JSON.stringify(updatedProjects));
}
});
I am trying to refresh my page on delete and refresh button, it gets new data but the issue is new data will add to old ones I need to clear old data before adding new data.
Code
addresses: any[] = [];
// loads first time data
this.addressService.getAddresses().subscribe((res) => {
for (let address of res['data']) {
this.addresses.push(address);
}
this.hideLoading();
});
// refresh list items
doRefresh(event) {
console.log('Begin async operation');
setTimeout(() => {
console.log('Async operation has ended');
// get new items in list
this.addressService.getAddresses().subscribe((res) => {
for (let address of res['data']) {
this.addresses.push(address);
}
this.hideLoading();
});
event.target.complete();
}, 2000);
}
//remove item and renew list items
removeAddress(id: string){
this.addressService.remove(id).subscribe(
data => {
this.alertService.presentToast(data['message']);
//getting items (renew)
this.addressService.getAddresses().subscribe((res) => {
for (let address of res['data']) {
this.addresses.push(address);
}
this.hideLoading();
});
},
error => {
this.alertService.presentToast(error['message']);
}
);
}
I think that I need to clear addresses: any[] = []; before getting
new items in my doRefresh(event){..} and removeAddress(id:
string){...} functions to avoid duplicates.
Any idea?
Assuming your refresh function works,
add this code before you get new items from your api
this.addresses =[];
or
this.addresses.length = 0;
For implementation wise, in regards to delete function, you can delete from your backend , clear your array and pull a fresh set of data which might be costly if you have a huge dataset.
You might want to consider updating your backend (delete that specific data) and removing that specific index from your array (when your delete function returns success)
For update, you can do a comparison and update those those array objects that has been modified. Else you can just clear your array and retrigger your retrieve api function.
How can I update certain properties of a local storage item or object as new data is inputted throughout the user journey and not lose what was previously entered or if the user decides to update?
My journey of 5 containers consisting of asking the user to input the following:
Name: string
Avatar: integer
Favourite Genres: multiple strings
On the first view I have created the local storage object / item that sets the name within the handleSubmit function.
handleSubmit(event) {
event.preventDefault();
//Profile object
let profile = { 'name': this.state.name, 'avatar': null, 'genres': '' };
// Put the object into storage
localStorage.setItem('profile', JSON.stringify(profile));
// Retrieve the object from storage
var retrievedObject = localStorage.getItem('profile');
//Log object
console.log('retrievedObject: ', JSON.parse(retrievedObject));
//On form submission update view
this.props.history.push('/profile/hello');
}
On my second view I want to update only the avatar property and maintain what the user had inputted in the previous view.
I'm doing this within the handleSelect function like so:
handleSelect(i) {
let selectedAvatarId;
let avatars = this.state.avatars;
avatars = avatars.map((val, index) => {
val.isActive = index === i ? true : false;
return val;
});
this.setState({
selectedAvatarId: selectedAvatarId
})
//Profile object
let profile = { 'avatar': i };
//Update local storage with selected avatar
localStorage.setItem('profile', JSON.stringify(profile));
}
You will need to read the existing value from localStorage, parse it as JSON and then manipulate the data, and write it back. There are numerous libraries out there for easily working with localStorage, but something along the lines of this should work as a generic function:
function updateProfile = (updatedData) => {
const profile = JSON.parse(localStorage.getItem('profile'));
Object.keys(updatedData).forEach((key) => {
profile[key] = updatedData[key];
});
localStorage.setItem('profile', JSON.stringify(profile));
}
If you use object spread, it could look a lot cleaner too:
function updateProfile = (updatedData) => {
const profile = {
...JSON.parse(localStorage.getItem('profile')),
...updatedData
};
localStorage.setItem('profile', JSON.stringify(profile));
}
There should probably be some safety checks in the above code, but hopefully gives you an idea for a starting point.
The only option as far as I know is to get it as a Json, amend accordingly and then save it is again.
I have a complicated data structure being built by queries on multiple collections and published.
It is working great for the initial creation, and on my local machine all the changes observed are reflected in the client as expected. However, in my staging environment I get the following error from mini-mongo when a change is observed
Uncaught Error: When replacing document, field name may not contain '.'(…)
The publishing code looks like this, where pub is the this from a Meteor.publish and rootObj is a reference to an Object in memory which gets properties modified but never has it's reference destoryed.
function _republish(pub, rootId, rootObj, handles, startup) {
// cleanup handles
if (handles.foo) {
handles.foo.stop();
}
// some query which could depend on rootObj/other calculated values
let cursor = SubColl.find({_id: {$in: bar}});
handles.foo = cursor.observeChanges({
removed(_id) {
rootObj.bar = rootObj.bar.filter(o => o._id !== _id);
pub.changed('foobarbaz', rootId, {bar: rootObj.bar})
},
changed(_id, fields) {
const index = rootObj.bar.findIndex(line => line._id === _id);
const changed = {};
_.each(fields, (value, field) => {
rootObj.bar[index][field] = value;
changed[`bar.${index}.${field}`] = value;
});
pub.changed('foobarbaz', rootId, changed);
},
added(_id, fields) {
rootObj.bar.push(_.extend({}, fields, {_id}));
if (!startup) {
// deeper children stuff
pub.changed('foobarbaz', rootId, {bar: rootObj.bar});
}
}
});
// deeper children stuff
startup = false;
// if startup was true, expect caller to publish this
}
As we can see, the publish works fine when I'm pub.changeding on just bar, but attempting to update a specific subdocument field (e.g. bar.0.prop) results in the inconsistent behaviour
If possible I want to avoid re-publishing the whole of bar as it is huge compared to updating a simple property.
How can I publish the change to a single field of a subdocument?