I'm using vuejs and vue-apollo in my project.
I have to components : GroupList and GroupForm
The thing is that I first arrive on the list, and then when I click on one of the groups, I arrive in the form view of that group.
To do that, I have a button that opens the form view by giving the id of that group :
openGroupFormView (group, index) {
this.$router.push({ name: 'GroupFormView', params: { groupId: group.id } })
}
In the past, I used to pass the group object as paramater and it worked but I changed my mind and now I want to pass the id so that when the component GroupForm is opened, the graphql query to retrieve this group based on the id is executed. I want to do that in order to avoid apollo-cache inconsistencies in case where someone edited the same group in the meantime.
So in my GroupForm component I have :
My prop :
props: {
groupId: Number
}
My component query :
apollo: {
group: {
query: GROUP_QUERY,
variables () {
return {
id: this.groupId
}
},
..
}
The associated query beneath that :
const GROUP_QUERY = gql`
query ($id: ID) {
group (id: $id) {
id
name
usersCount
userIds {
id
username
}
}
}
`
My problem is that in data () of my component, I used to use the group that I passed through the props as an object. Now, I still want to do things on the group but the apollo part is executed after the data () part so I have an undefined problem. How could I ensure that this problem doesn't occur ? I mean that apollo query is run first.
My data() block :
data () {
return {
form: {
name: this.group === undefined ? '' : this.group.name
}
}
},
Thanks in advance for your help !
EDIT : A part of my template
<b-form>
<b-form-group label="Name">
<b-form-input
type="text"
v-model="form.name"
required
:disabled="!editing"/>
</b-form-group>
<div v-if="editing" class="mb-3">
<b-button #click="cancelEdit()">Cancel</b-button>
<b-button #click="group === undefined ? createGroup() : updateGroup(group)" variant="success">Save</b-button>
</div>
</b-form>
I don't use Apollo but it looks like you should be able to initialize name to an empty string and then use the result hook to set it when the query completes.
Untested:
apollo: {
group: {
...
result({ data, loading, networkStatus }) {
this.form.name = data.name // something like this
}
}
}
Related
I am trying to set checkboxes active based on query parameters using Sveltekit. To achieve this, I tried to get all route query parameters using the 'url.searchParams.entries()' method to get all query parameters from the URL.
Then I am trying to loop through all the params and then loop through all posts and check if the value of the query param is equal to a value in posts. If so, it should set checked to true forEach checkbox that meets the value. Then in my HTML, I bind the checked variable to my checkbox. But the issue is that when I put in 4 in the query params (or select 4 using the checkbox), it checks all the checkboxes but, if I select any of the other checkboxes, it doesn't do a single thing (besides from querying data from the JSON placeholder api)
This is my HTML:
<div class="grid grid-cols-2">
{#each posts as post}
<div class="col-span-1">
<input type="checkbox" bind:checked={checked} value={post.id} bind:group=
{selectedPosts} on:change="{() => updateValue(post.id)}" class="mr-2">
</div>
<div class="col-span-1">
<label for="{post.id}">{post.id}</label>
</div>
{/each}
</div>
This is inside my script tag:
import { filter, results } from '../../stores.js';
import { page } from '$app/stores';
let checked = false
let selectedPosts = []
const posts = [
{
name: 'post',
id:'1'
},
{
name: 'post',
id: '2'
},
{
name: 'post',
id: '3'
},
{
name: 'post',
id: '4'
}
]
$: {
//If the query params would be '?posts=1', the output would be ['posts', '1']
let params = [...$page.url.searchParams.entries()]
params.forEach(param => {
//Returns the first param. In this case '1'
let value = param[1]
posts.forEach(post => {
if(post.id === value){
console.log('true')
checked = true
} else {
checked = false
console.log('false')
}
})
});
}
async function updateValue(value) {
filter.set(value)
}
In my store I set the query params like so:
export const results = derived(filter, (value) => {
if(!value) return {};
const data = fetch(`https://jsonplaceholder.typicode.com/todos/${value}`).then(res => {
return res.json()
})
return data
});
filter.subscribe(value => {
if(!value) return
goto(`/activiteiten/filter?post=${value.toString()}`)
return value
})
And finally I return with a derived store some data from the JSON placeholder API.
You don't need bind:checked={checked} when using bind:group. I think this is the root of your problem. You are setting the same checked variable for every item.
if(post.id === value){
console.log('true')
checked = true
} else {
checked = false
console.log('false')
}
When you later check based on this variable, you get unexpected behavior.
I am trying to build a search box. When you type in it, it automatically filters my App.js file (Cards of robots).
render() {
const filteredRobots = this.state.robots.filter(robots => {
return robots.name.toLowerCase().includes(this.state.searchfield.toLowerCase())
})
return(
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={this.onSearchChange}/>
<CardList robots={filteredRobots}/>
</div>
)
}
I have a separate, robots.js file in which I list the properties of the robots:
export const robots = [
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere#april.biz'
},
My filter method works, when I only put a single property in it. In the code above, I am illustrating it when it is filtered by name. I would like to include the "email" property as well, so that when I type in the search field, it also filters by the email and not just the name.
Thanks in advance, this is my first post, sorry for not being clear, I recently started learning.
You can combine the objects fields into a single string and use it instead of using the name
const filteredRobots = this.state.robots.filter(robot => {
return Object.values(robot).join(" ").toLowerCase().includes(this.state.searchfield.toLowerCase())
})
Now, all the robot fields (name, username, email, id) are considered in your search function.
filter method passes the array items directly.
const searchedString = this.state.searchfield.toLowerCase()
const filteredRobots = robots.filter(robotItem => robotItem.name.toLowerCase().includes(searchedString) || robotItem.username.toLowerCase().includes(searchedString) )
more about the filter method in mdn docs
I am trying to make a search bar to collect a list of products which the user will then be able to select an array of products and add it to an order.
So far the products can be searched, added to a "products" array via an "add" button and they are able to see the data within products. They are also able to remove products that they do not want.
My issue is that I am trying to send the data from the "products" array with a parent form. The search component is located within the form as well as the list. But I do not know how I would send the array with the form.
I do not need to send the entire product object, but just the ID which will be used to link up the products with the order.
Here is where the component is:
<div class="uk-margin" id="search">
<label for="Search" class="uk-form-label">Search products to add</label>
<search input="ess"></search>
</div>
Here is the vuejs component data:
export default {
data() {
return {
keywords: null,
results: [],
products: []
};
},
watch: {
keywords (after, before) {
this.fetch();
}
},
methods: {
fetch() {
axios.get('/search', { params: { keywords: this.keywords } })
.then(response => this.results = response.data)
.catch(error => {});
},
addProduct (product) {
let duplicate = false;
this.products.forEach((item, index) => {
if (item.ID === product.ID) {
duplicate = true;
}
});
if (!duplicate) {
this.products.push(product);
}
},
removeProduct (product) {
Vue.delete(this.products, product);
}
}
}
Everything works fine, but my question is.. How am I able to pass the data back to the html / laravel to use it while sending data to the controller. I only need to send the "products" array, I have tried using input with the data but it isn't working. Does anyone know how I could do it, or is the best way to do so, using JavaScript and add them to an array by finding all the elements which are being displayed?
Many thanks.
I have a screen where I run a query and render some items based on the results:
query getMyFavoritePlaces($myID: String) {
favorite_place(where: { user_id: { _eq: $myID } }) {
id
location
label
user{
id
}
user_id
}
}
const { data, error } = useGetMyFavoritePlacesQuery({ variables: { myID: profile.id }});
{
data && (
<FlatList
data={data.favorite_place}
renderItem={({ item }) => <FavoriteLocationEntry place={item} />}
keyExtractor={(item) => item.id.toString()} />
)
}
When I run this mutation by clicking on any of the items from the flatlets:
mutation RemoveFavoriteLocation($id: Int!) {
delete_favorite_place_by_pk(id: $id) {
id
location
label
user{
favorite_places {
id
location
label
}
}
user_id
}
}
The object gets deleted successfully and the UI is also updated. However, I get a warning that
Cache data may be lost when replacing the favourite_place field of a Query object.
To address this problem define a custom merge function for the Query.favorite_place field, so InMemoryCache can safely merge these objects: { ... }
The values shown here are the ids. I am already returning ids in both, the query and mutation. How can I fix it then?
It might not be possible to run the favourite_place query itself within the mutation.
Your mutation needs to include an id field for the user or it may mistakenly delete a favourite_place field for a different user:
mutation RemoveFavoriteLocation($id: Int!) {
delete_favorite_place_by_pk(id: $id) {
id
location
label
user{
**id**
favorite_places {
id
location
label
}
}
user_id
}
}
P.s please don't include the asterisks, just there to bring focus to the missing property.
In this app im building i have a little problem referring to adress the SPA to the selected item by its id.
Fetching my data to my end point(action) i got a array of objects , then through a getter i just grab all that data, and using the function find() i filter all the array throwing the object which contains the id referrig to the selected one.Lets say:
First- Exposing on the router the link and the condition of the id which demark the object to select
{
path: '/game/selected/:gameCrabId',
name: 'GameSelectedView',
props:true,
component: GameSelectedView,
},
Second- Eexposing the component where in i access through router link the supposed object which contains the requested id
<v-btn router-link :to="`/game/selected/${game.game_id}`" >Access</v-btn>
Third-Initializing the view in general of the supposed selected object
<template>
<v-container v-if="allGames">
<!-- <div>{{gameSelected}}</div> -->
</v-container>
</template>
<script>
import { mapActions, mapGetters, mapState } from "vuex";
export default {
name: "GameSelectedview",
props: ["gameCrabId"],
data() {
return {};
},
// props:{SelectedGameComponent:Object},
methods: {
...mapActions(["allGames"])
},
computed: {
...mapGetters(["getSelectedGame"]),
gameSelected() {
return this.getSelectedGame(this.gameCrabId);
}
},
async created() {
await this.allGames;
await this.gameSelected;
await console.log(this.gameSelected);
},
</script>
then on my state managment component (Vuex) , i trigger the method getter which eventually brings me once i click that button of step 2 , the object which has the same id than the required one, but first im exposing the state where in that array of objects is
state: {
allGamesData: {all_games:[
{"game_id": 1,"game_player": "Romi","game_score": 0},
{"game_id": 2,"game_player": "Messi","game_score": 0},
{"game_id": 3,"game_player": "CR7","game_score": 0},
]
},
},
getters:{
getSelectedGame: (state) => (gameCrabId) => {
console.log(gameCrabId);
return state.allGamesData.all_games.find((gameID) => {
gameID.game_id === gameCrabId;
});
},
}
And this is the getter which eventuallly accesses the state and that array of objects , and using a double arrow function first aims to the state ,and second through the parameter (gameCrabId)which is the once that expose the id so neccesary to complete the link to that selected item;then returning the access to that array of objects in my state , i just use the function find() to establish a comparison between that id carried by the parameter (gameCrabId) and the array of objects brought from the state , breaking the cycle once find the first comparable item in the objects which match with that id gameID.game_id === gameCrabId
In order to see if ,my id carrier works i set a log , and works , the number is brought , but for any reason the filter find() throws me undefined in the object selected despite of already have the id once the comparison get settled, being imposible retrieve any information of that object
Missing return from find()
The callback to Array.prototype.find() must return a Boolean, but currently it returns nothing, so the result of find() would always be null:
return state.allGamesData.all_games.find((gameID) => {
gameID.game_id === gameCrabId; // FIXME: No return statement
});
Mismatched types
game_id is a Number, but gameCrabId is a string, and getSelectedGame() uses === to compare the two, which requires the operands to have the same type. One solution is to perform explicit type conversion to ensure both operands are Number:
getters: {
getSelectedGame: state => gameCrabId => {
return state.allGamesData.all_games.find((gameID) => {
// return gameID.game_id === gameCrabId; // DON'T DO THIS
return gameID.game_id === Number(gameCrabId);
});
}
}
demo