With Meteor (1.4.2.3) and React, I have the collection Objects which has an itemId which refers to the collection Items.
Currently I subscribe to the collection on the client side with:
export default createContainer(() => {
let objectsSub = Meteor.subscribe('allObjects');
var objects = Objects.find({}, {
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
}).fetch();
return {
objects: objects,
}
}, App);
This works perfect, but I think it is more elegant to merge the collections on the server side. However, none of the solutions I found seem to work
Transform at collection definition
const Objects = new Mongo.Collection('objects',
{
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
})
}
});
The console gives:
Error: transform must return object
Transform at publish
if (Meteor.isServer) {
Meteor.publish('allObjects', function () {
return Objects.find({}, {
sort: { startedAt: -1 },
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
});
});
};
TypeError: Cannot read property 'name' of undefined
Where name is a property of Items
i usually do it in the publish like this:
Meteor.publish('allObjects', function () {
let cursor = Objects.find({}, {
sort: { startedAt: -1 });
});
let transformData = (fields) => {
fields.item = Items.findOne({
_id: fields.itemId
});
return fields;
};
let handle = cursor.observeChanges({
added: (id, fields) => {
fields = transformData(fields);
this.added('objects', id, fields);
},
changed: (id, fields) => {
fields = transformData(fields);
this.changed('objects', id, fields);
},
removed: (id) => {
this.removed('objects', id);
}
});
this.ready();
this.onStop(() => {
handle.stop();
});
}
Related
I'm trying to find the right way to create and consequently update inside a map function.
These are the steps I need:
Map function "reads" the array of elements ids
Create new record on "leads_status" table
Using the new record id (from "leads_status") "leads" table is updated using "leads_status.id" as foreign key related to "leads.id_ls"
This is the code I tried.
const [create, { isLoading: isLoadingCreate, error: errorCreate }] = useCreate();
const [record, setRecord] = React.useState(null);
leadsIDS.map((value, index) => {
create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
}, {
onSuccess: ({ id }) => {
setRecord([id, value]);
},
onError: () => {
console.log();
}
});
update('leads', {
id: record[1],
data: {
id_ls: record[0]
}
}, {
enabled: !isLoadingCreate && record !== null
}, {
onSuccess: () => {
console.log(record);
},
onError: error => notify('Error', { type: 'warning' })
})
})
I tried also to put the "update" function inside the "create --> onSuccess" but also there the code is not working as I want.
In "leads_status" table records are always created for each element in "leadsIDS" array but in "leads" table only 1 records is updating.
Where am I wrong?
The useCreate and useUpdate hooks are designed for single actions. If you want to chain several actions, I suggest you use the useDataProvider hook, instead, which lets you manipulate Promises.
const dataProvider = useDataProvider();
const notify = useNotify();
try {
await Promise.all(leadsIDS.map(async (value, index) => {
const { data: leadStatus } = await dataProvider.create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
});
await dataProvider.update('leads', {
id: value,
data: { id_ls: leadStatus.id }
});
}));
} catch (e) {
notify('Error', { type: 'warning' });
}
I use Strapi V4. I have a link collection and I want to update likes.
How update the relation array ? When I put new data old value are replace by the new one.
Example :
likes : [1]
if I update another time
likes:[2].
BUT I want this likes : [1,2]
I try this but It d'oesn't work. Thans for your replay
'use strict';
/**
* link controller
*/
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::link.link', ({ strapi }) => ({
// Method 2: Wrapping a core action (leaves core logic in place)
async find(ctx) {
const { data, meta } = await super.find(ctx);
const linkId = data.map((link) => link.id);
const allPosts = await strapi.entityService.findMany('api::link.link', {
fields: ["id"],
filters: { id: { $in: linkId } },
populate: {
likes: { count: true },
},
});
data.forEach(link => {
link.likes = allPosts.find(({ id }) => id === link.id)?.likes?.count || 0;
});
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});
return { data, meta };
},
}));
This part need to be fix. Can you help me ? Thanks
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});
Hy I'm using the Apollo Client with React. I query the posts with many different variables. So I have one post in different "caches". Now I want to delete a post. So I need to delete this specific post from all "caches".
const client = new ApolloClient({
link: errorLink.concat(authLink.concat(httpLink)),
cache: new InMemoryCache()
});
Postquery:
export const POSTS = gql`
query posts(
$after: String
$orderBy: PostOrderByInput
$tags: JSONObject
$search: String
$orderByTime: Int
) {
posts(
after: $after
orderBy: $orderBy
tags: $tags
search: $search
orderByTime: $orderByTime
) {
id
title
...
}
}
`;
I tried it with the cache.modify(), which is undefined in my mutation([https://www.apollographql.com/docs/react/caching/cache-interaction/#cachemodify][1])
const [deletePost] = useMutation(DELETE_POST, {
onError: (er) => {
console.log(er);
},
update(cache, data) {
console.log(cache.modify())//UNDEFINED!!!
cache.modify({
id: cache.identify(thread), //identify is UNDEFINED + what is thread
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
}
}
})
}
});
I also used the useApolloClient() with the same result.
THX for any help.
Instead of using cache.modify you can use cache.evict, which makes the code much shorter:
deletePost({
variables: { id },
update(cache) {
const normalizedId = cache.identify({ id, __typename: 'Post' });
cache.evict({ id: normalizedId });
cache.gc();
}
});
this option worked for me
const GET_TASKS = gql`
query tasks($listID: String!) {
tasks(listID: $listID) {
_id
title
sort
}
}
`;
const REMOVE_TASK = gql`
mutation removeTask($_id: String) {
removeTask(_id: $_id) {
_id
}
}
`;
const Tasks = () => {
const { loading, error, data } = useQuery(GET_TASKS, {
variables: { listID: '1' },
});
сonst [removeTask] = useMutation(REMOVE_TASK);
const handleRemoveItem = _id => {
removeTask({
variables: { _id },
update(cache) {
cache.modify({
fields: {
tasks(existingTaskRefs, { readField }) {
return existingTaskRefs.filter(
taskRef => _id !== readField('_id', taskRef),
);
},
},
});
},
});
};
return (...);
};
You can pass your updater to the useMutation or to the deletePost. It should be easier with deletePost since it probably knows what it tries to delete:
deletePost({
variables: { idToRemove },
update(cache) {
cache.modify({
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
},
},
});
},
});
You should change variables to match your mutation. This should work since posts is at top level of your query. With deeper fields you'll need a way to get the id of the parent object. readQuery or a chain of readField from the top might help you with that.
I have a unit test that is producing something I didn't expect:
Background: I'm making a simple todo list with Angular/test driven development.
Problem: When I call editTask on an item in the array, it changes the item's value. But, I don't see how it's changed in the original array because the original array is never accessed in the method I'm testing. Please help me connect HOW the original array is being changed? It seems Object.assign is doing this, but why?
describe('editTask', () => {
it('should update the task by id', () => {
const dummyTask1 = { id: 1, name: 'test', status: false };
service.tasks.push(dummyTask1); //refers to TestBed.get(TaskService)
const id = 1;
const values = { name: 'cat', status: false };
service.editTask(id, values);
console.log(service.tasks); // why does this log this object? [Object{id: 1, name: 'cat', status: false}]
expect(service.tasks[0].name).toEqual(values.name); // Test passes
});
});
Here is the method I'm testing:
editTask(id, values) {
const task = this.getTask(id);
if (!task) {
return;
}
Object.assign(task, values); //How does this line change the array?
return task;
}
getTask(id: number) {
return this.tasks.filter(task => task.id === id).pop(); //is this altering the original array somehow?
}
If needed, here's the full Angular service:
export class TaskService {
tasks: any = [];
lastId = 0;
constructor() { }
addTask(task) {
if (!task.id) {
task.id = this.lastId + 1;
}
this.tasks.push(task);
}
editTask(id, values) {
const task = this.getTask(id);
if (!task) {
return;
}
Object.assign(task, values);
return task;
}
deleteTask(id: number) {
this.tasks = this.tasks.filter(task => task.id !== id);
}
toggleStatus(task) {
const updatedTask = this.editTask(task.id, { status: !task.status});
return updatedTask;
}
getTasks() {
return of(this.tasks);
}
getTask(id: number) {
return this.tasks.filter(task => task.id === id).pop();
}
}
Here is the github repo: https://github.com/capozzic1/todo-tdd
The getTask() method is getting a reference to the item in the array using the array filter() method.
It then uses Object.assign() to change the properties of the item. The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
So now the values of the reference in memory of the item is changed. Because it is a reference in memory you will see the original item being changed.
I would like to add an array into an array object. However, I am only able to add the array into a new object instead of the existing one. Is there any way where I can link the array to the existing object? Its much more easier for me too if I could add in the array based on which link it is from.
Here is my code:
this.af.database.list(`/users/${userid}/favourites`, {
query: { orderByChild: 'priority' },
preserveSnapshot: true
})
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
this.category.push({
name: snapshot.val().name,
rss: snapshot.val().regions[0].rss
});
})
for (let i = 0; i < this.category.length; i++) {
this.http.get(this.category[i].rss)
.map(res => res.text())
.subscribe((data) => {
this.parseXML(data)
.then((data) => {
this.xmlItemsApac = data
this.category.push({
feeds: data
})
});
});
}
});
console.log(this.category)
parseXML(data) {
return new Promise(resolve => {
var k,
arr = [],
parser = new xml2js.Parser(
{
explicitArray: false
});
parser.parseString(data, function (err, result) {
var obj = result.rss.channel;
for (k in obj.item) {
var item = obj.item[k];
arr.push({
title: item.title,
link: item.link,
description: item.description,
pubDate: item.pubDate
});
}
resolve(arr);
});
});
}