I have a search engine on my page in which when looking for a location I add a marker which the user can move, the problem is that every time I look for a new location, it adds a marker for each one, how can I solve this so that only show me a markup every time I search for a location?
This is the code that does this
const geocoder = new MapboxGeocoder({
mapboxgl: mapboxgl,
accessToken: mapboxgl.accessToken,
marker: false
})
geocoder.on('result', e => {
const marker = new mapboxgl.Marker({
draggable: true
})
.setLngLat(e.result.center)
.addTo(map)
marker.on('dragend', (e) => {
})
})
map.addControl(geocoder)
Related
I need to create a custom ghost image for a dragged element. And also I need to change it's style a bit. This is my code:
const onDragStart = (e) => {
const ghost = e.target.cloneNode(true);
ghost.setAttribute("id", "ghost");
ghost.style.position = "absolute";
ghost.style.right = "-999px";
// counter is an element that should not be displayed on a ghost image
ghost.getElementsByClassName("counter")[0].remove();
document.body.appendChild(ghost);
e.dataTransfer.setDragImage(ghost, 20, 20);
};
const onDragEnd = () => {
const ghost = document.getElementById("ghost");
if (ghost) {
ghost.remove();
}
};
Since I use Vue, manipulating DOM directly seems like a bad idea. Is there any way to do the same result, but in a Vue-way?
I am developing an app, which has sidebar menu. I have an atom, which saves the state of the /menu and an atom which saves the last selected menu key (as this key is used for other selectors too) -> for getting specific info for the current selected key.
export const menuItems = atom({
key: "menuItems",
default: ({ get }) => get(baseApi)("/menu"),
}); -> Returns Menu Items
And then I have an atom, which saves the selected menu item key:
export const selectedMenuKey = atom<string>({
key: "selectedMenuKey",
});
I cannot prefix the initial selected menu key as I don't know it in advance. I want the behavior to be following:
If the key is not set (when the app initially runs) set the selectedMenuKey value to be the first item of the menuItems atom value, otherwise be whatever is set last.
What would you say is the best way to achieve this?
I ran into this exact use case. Here is what I ended up doing.
In my 'activeTabState' file, equivalent to your 'selectedMenuKey':
import { atom, selector } from 'recoil';
import formMapState from './formMapState';
const activeTabState = atom({
key: 'activeTabAtom',
default: selector({
key: 'activeTabDefault',
get: ({ get }) => {
const formMap = get(formMapState);
if (!formMap) return null;
const [defaultTab] = Object.keys(formMap);
return defaultTab;
},
}),
});
export default activeTabState;
Then you can update the tab just like any other recoil state:
const FormNavigationTab = (props) => {
const { text, sectionName } = props;
const [activeTab, setActiveTab] = useRecoilState(activeTabState);
return (
<NavigationTab active={activeTab === sectionName} onClick={() => setActiveTab(sectionName)}>
{text}
</NavigationTab>
);
};
One thing to watch out for is that your activeTab value will be null until the menu items are loaded. So based on my use case, I needed to add a safeguard before using it:
const FormPage = () => {
const map = useRecoilValue(formMapState);
const activeTab = useRecoilValue(activeTabState);
// Starts out null if the map hasn't been set yet, since we don't know what the name of the first tab is
if (!activeTab) return null;
const { text, fields, sections } = map[activeTab];
// ... the rest of the component
I have this weird vuejs effect where when I am adding a new object data, the v-for re-renders all the object even if its already rendered.
I am implementing an infinite scroll like facebook.
The Code
To explain this code, I am fetching a new data from firebase and then push the data into the data object when it reaches the bottom of the screen
var vueApp = new Vue({
el: '#root',
data: {
postList: [],
isOkaytoLoad: false,
isRoomPostEmpty: false,
},
mounted: function() {
// Everytime user scroll, call handleScroll() method
window.addEventListener('scroll', this.handleScroll);
},
methods: {
handleScroll: function()
{
var d = document.documentElement;
var offset = d.scrollTop + window.innerHeight;
var height = d.offsetHeight - 200;
// If the user is near the bottom and it's okay to load new data, get new data from firebase
if (this.isOkaytoLoad && offset >= height)
{
this.isOkaytoLoad = false;
(async()=>{
const lastPost = this.postList[this.postList.length - 1];
const room_id = PARAMETERS.get('room');
const q = query(collection(db, 'posts'), where('room', '==', room_id), orderBy("time", "desc"), limit(5), startAfter(lastPost));
const roomSnapshot = await getDocs(q);
roomSnapshot.forEach((doc) => {
const postID = doc.id;
(async()=>{
// Put the new data at the postList object
this.postList = [...this.postList, doc];
const q = query(collection(db, 'comments'), where('post_id', '==', postID));
const commentSnapshot = await getDocs(q);
doc['commentCount'] = commentSnapshot.size;
//this.postList.push(doc);
console.log(this.postList);
setTimeout(()=>{ this.isOkaytoLoad = true }, 1000);
})();
});
})();
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div v-if="postList.length > 0" class="card-containers">
<!-- I have a component `Postcard` in my js file and it works well -->
<Postcard
v-for="post in postList"
:key="post.id"
:post-id="post.id"
:owner-name="post.data().owner_displayName"
:owner-uid="post.data().owner_id"
:post-type="post.data().post_type"
:image-url="post.data().image_url"
:post-content="truncateString(linkify(post.data().post_content))"
:room="post.data().room"
:time="post.data().time.toDate()"
:likers="post.data().likers"
:comment-count="post.commentCount"
:file-url="post.data().file_url"
:file-name="post.data().file_name"
:downloads="post.data().downloads">
</Postcard>
</div>
Now, the problem is here ...
Look at this screen record, FOCUS AT THE MOUSE, it's lagging and I can't even click on those buttons when vuejs is adding and loading the new data
Here is the code That I used
What I suspect
I am suspecting that everytime I add a new data, the VueJS re-renders it all, which does that effect. How can I force vueJS to not re-render those data that is already rendered in the screen?
You've got two unnecessary async IIFE; the second one inside the forEach is particularly problematic because the async code inside it will be executed concurrently for each loop iteration, which has implications:
getDocs() will be fired all at once for each loop ieration, potentially spamming the server (assuming this is performing a network request). Was this your intention? It looks like you're only fetching at most 5 new posts, so this is probably OK.
The async function updates some state which will trigger Vue to re-render for each doc. This should be batched together at the end so Vue does as minimal updates as possible.
Also don't use var; use const or let instead. There's almost no good reason to use var anymore, let it die.
I can't say this will improve your performance substantially, but I recommend making the following changes (untested):
async handleScroll() {
const d = document.documentElement;
const offset = d.scrollTop + window.innerHeight;
const height = d.offsetHeight - 200;
// If the user is near the bottom and it's okay to load new data, get new data from firebase
if (this.isOkaytoLoad && offset >= height) {
// Prevent loading while we load more posts
this.isOkaytoLoad = false;
try {
// Get new posts
const lastPost = this.postList[this.postList.length - 1];
const room_id = PARAMETERS.get('room');
const q = query(collection(db, 'posts'), where('room', '==', room_id), orderBy("time", "desc"), limit(5), startAfter(lastPost));
const roomSnapshot = await getDocs(q);
// Fetch comments of each post. Do this all at once for each post.
// TODO: This can probably be optimized into a single query
// for all the posts, instead of one query per post.
await Promise.all(roomSnapshot.docs.map(async doc => {
const postID = doc.id;
const q = query(collection(db, 'comments'), where('post_id', '==', postID));
const commentSnapshot = await getDocs(q);
doc.commentCount = commentSnapshot.size;
}));
// Append the new posts to the list
this.postList.push(...roomSnapshot.docs);
} catch (ex) {
// TODO: Handle error
} finally {
// Wait a bit to re-enable loading
setTimeout(() => { this.isOkaytoLoad = true }, 1000);
}
}
}
Doing :post-content="truncateString(linkify(post.data().post_content))" in the template means linkify will be executed during each re-render. I suspect linkify may be potentially slow for long lists? Can this be pre-calculated for each post ahead of time?
You're registering a window scroll event listener when the component is mounted. If the component is ever destroyed, you need to unregister the event listener otherwise it'll still fire whenever the window is scrolled. This may not be an issue in your case, but for reusable components it must be done.
I have tried searching everywhere, from stackoverflow to GitHub but i can get a solution. I am trying to get list of users by using their userid that I get from a collection of businesses. What Am i doing wrong?
componentWillMount() {
//Loading all the business collections.
firebase.firestore().collection("business").onSnapshot((snapshot) => {
var bizs = [];
snapshot.forEach((bdt) => {
var userdt = [];
//get document id of a certain user in the business collections
firebase.firestore().collection('users').where("userid", "==", bdt.data().userid).get()
.then((snap) => {
snap.forEach(dc => {
//loading details of the user from a specific ID
firebase.firestore().collection("users").doc(dc.id).onSnapshot((udt) => {
userdt.push({
name: udt.data().fullname,
photourl: udt.data().photoURL,
location: bdt.data().location,
openhrs: bdt.data().openHrs,
likes: '20',
reviews: '3002',
call: bdt.data().contacts
});
console.log(userdt); //this one works
})
console.log(userdt); // but this one doesnt diplay anything just []
})
}).catch((dterr) => {
console.log(dterr)
})
});
this.setState({bizdata: bizs,loading: false
});
});
}
I am using react-native and firestore
Put log with some number,
like,
console.log('1',userdt);
console.log('2',userdt);
and check weather which one is appearing first, Maybe '2' is executing before updating the data
I have this method to get data from an API, which sends me information of many furniture pieces:
loadPieces() {
this.isLoading = true;
axios.get(this.galleryRoute)
.then(r => {
this.gallery = r.data;
this.isLoading = false;
})
.catch(error => {
this.$nextTick(() => this.loadPieces());
});
console.log(this.galleryRoute);
},
This is a part of the response I get, which represents only one piece:
[[{"id":266,"name":" Tray 7x45x32, white stained ash","thumbnail":{"width":840,"height":840,"urls":{"raw":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a.jpeg","small":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#140.jpeg","medium":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#420.jpeg"}}},
Now I want to create a filter so that I can get a specific piece from the JSON object, using it's id. I've tried searching but so far I have no idea how to do this.
Thanks in advance!
Add a computed property which applies the filter to this.gallery:
computed: {
filteredGallery() {
if (!this.gallery) return []; // handle gallery being unset in whatever way
return this.gallery.filter(picture =>
// some reason to show picture
);
}
}
I'm assuming gallery is an array, but you could apply a similar technique to it if it was an object, using e.g. Object.keys(this.gallery).
Then in your template, use filteredGallery instead of gallery.