I cant figure out how to REMOVE root element from my store. I can delete and modify and set to null everything else, but I can't figure this one out.
I have received container from my server, but now I need to remove it.
Couple of ways I have tried:
const stateLink = withClientState({
cache,
resolvers: {
Mutation:{
localDeleteContainer: (_, data, { cache }) => {
cache.writeQuery({
query:gql`{
container
}`
,data:{
container:undefined
}
});
cache.writeData({ data: {container:undefined}});
return false;
},
}
},
defaults: {
},
});
I tried to parse entire cache then remove the object and overwrite it completely, but that messed up the store completely.
Related
I currently have the following code in a codebase using "#apollo/client": "^3.4.17",
const getFrontEndApiSchema = async (authToken: string, hostname: string) => {
const executor = async ({
document,
variables,
}: Parameters<Parameters<typeof introspectSchema>[0]>[0]) => {
const fetchResult = await crossFetch(`${resolveApiUri(hostname)}/graphql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authentication-Token': authToken,
},
body: JSON.stringify({ query: print(document), variables }),
})
return fetchResult.json()
}
return makeExecutableSchema({
typeDefs: wrapSchema({
schema: buildClientSchema(await unzipSchema()),
executor,
}),
})
}
export const getSchema = async () => {
const frontEndSchema = await getFrontEndApiSchema()
return stitchSchemas({
subschemas: frontEndSchema ? [frontEndSchema, schema] : [schema],
mergeDirectives: true,
})
}
const apolloClient = createApolloClient(
{
schema,
rootValue: { request: req },
},
getAuthenticationToken(req),
false,
)
Which works and fires off requests. However we noticed during a Telemetry exercise (whereby we are trying to track traces through individual operations in DataDog / NewRelic) that a single operation is effectively being split up into it's constituent queries and sent without it's parent operation name.
It's not so clear to me from reading the docs why I would need to this executor function for graphql queries rather than the standard Apollo link chain (similar to what i'm using for the client side apollo client).
So I removed the unneeded executor function to the following.
makeExecutableSchema({
typeDefs: wrapSchema({
schema: buildClientSchema(await unzipSchema()),
}),
})
This worked in so far as the operations where being made and return a result, however ostensibly it was returning results which matched those which would be returned if unauthenticated, (i.e. no authentication token set in the header).
I've checked my error link and have logged context headers and it appears to have the token.
I've also tried swapping the Schemalink for a normal link with no success.
export default function createApolloClient(
schema: SchemaLink.Options,
token: string,
isTest?: boolean,
) {
const link = from([
authLink(token),
serverErrorLink(),
...(__DEV__ ? [logLink(true)] : []),
new SchemaLink(schema),
])
return new ApolloClient({
link,
cache: createCache(),
ssrMode: true,
queryDeduplication: true,
...(!isTest && {
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
},
query: { fetchPolicy: 'cache-first' },
},
}),
})
}
A typical graphql operation I'm sending
query myOperationName{
user {
id
firstName
}
query2{
id
}
query3{
id
}
}
When I do print(document) in the body of my original executor function I am getting
query2{
id
}
etc
So my question is how server side do I construct the correct Apollo client/ link chain combo such that operations are not stripped of their operation names? And any additional clarity on whether it's necessary to use the SchemaLink at all if my express server is on a different box to the api it talks to would be helpful
After making a mutation the UI does not update with a newly added item until the page is refreshed. I suspect the problem is in the update section of the mutation but I'm not sure how to troubleshoot further. Any advice is much appreciated.
Query (separate file)
//List.js
export const AllItemsQuery = gql`
query AllItemsQuery {
allItems {
id,
name,
type,
room
}
}
`;
Mutation
import {AllItemsQuery} from './List'
const AddItemWithMutation = graphql(createItemMutation, {
props: ({ownProps, mutate}) => ({
createItem: ({name, type, room}) =>
mutate({
variables: {name, type, room},
optimisticResponse: {
__typename: 'Mutation',
createItem: {
__typename: 'Item',
name,
type,
room
},
},
update: (store, { data: { submitItem } }) => {
// Read the data from the cache for this query.
const data = store.readQuery({ query: AllItemsQuery });
// Add the item from the mutation to the end.
data.allItems.push(submitItem);
// Write the data back to the cache.
store.writeQuery({ query: AllItemsQuery, data });
}
}),
}),
})(AddItem);
Looks promising, one thing that is wrong is the name of the result of the mutation data: { submitItem }. Because in the optimistic Response you declare it as createItem. Did you console.log and how does the mutation look like?
update: (store, {
data: {
submitItem // should be createItem
}
}) => {
// Read the data from our cache for this query.
const data = store.readQuery({
query: AllItemsQuery
});
// Add our comment from the mutation to the end.
data.allItems.push(submitItem); // also here
// Write our data back to the cache.
store.writeQuery({
query: AllItemsQuery,
data
});
}
I'm not entirely sure that the problem is with the optimisticResponse function you have above (that is the right approach), but I would guess that you're using the wrong return value. For example, here is a response that we're using:
optimisticResponse: {
__typename: 'Mutation',
updateThing: {
__typename: 'Thing',
thing: result,
},
},
So if I had to take a wild guess, I would say that you might want to try using the type within your return value:
optimisticResponse: {
__typename: 'Mutation',
createItem: {
__typename: 'Item',
item: { // This was updated
name,
type,
room
}
},
},
As an alternative, you can just refetch. There have been a few times in our codebase where things just don't update the way we want them to and we can't figure out why so we punt and just refetch after the mutation resolves (mutations return a promise!). For example:
this.props.createItem({
... // variables go here
}).then(() => this.props.data.refetch())
The second approach should work every time. It's not exactly optimistic, but it will cause your data to update.
I have code for generate checkboxes list:
accountsCheckboxes: Ember.computed('accountsCheckboxes.#each', function(){
return this.model.accounts.map(row => {
return {
label: row.get('name'),
value: row.get('id')
};
})
}),
but after modify accounts collection, add or remove, this computed property doesnt refresh. I tried find how to do it with events, or how to observe store collection, but without success.
I modyfy this model collection in others controllers.
Its a little confusing what you're trying to do by observing the same property you're defining:
// accountsCheckboxes observes accountsCheckboxes?
accountsCheckboxes: Ember.computed('accountsCheckboxes.#each', ...)
This won't work and will probably result in an infinite chain of lookups.
Did you mean to observe model.accounts instead? If so, this is what you could've done:
accountsCheckboxes: Ember.computed('model.accounts.#each.name', function() {
return this.get('model.accounts').map(row => {
return {
label: row.get('name'),
value: row.get('id')
};
})
});
Note that you must call this.get('model'), not this.model to make sure you always get the proper data.
Alternatively, you might use Ember.computed.map:
accountsCheckboxes: Ember.computed.map('model.accounts.#each.name', function(row) {
return {
label: row.get('name'),
value: row.get('id')
};
});
Every thing work fine in this function, but this.nav.popToRoot() doesn't work in that location.
If I move it to the beginning of the function it works correctly.
Doesn't any one has a logical explanation for this.
Thank you in advance for your time and consideration.
Here is the code in a booking.ts component:
book(){
let newReservation = {
_id: this.room._id,
from: this.details.from.substring(0,10),
to: this.details.to.substring(0,10)
}
let loading = this.loadingCtrl.create({
content: "Booking room..."
});
loading.present();
this.roomsService.reserveRoom(newReservation).then((res) => {
loading.dismiss();
console.log("Room reserved successfully ... ");
this.nav.popToRoot();
}, (err) => {
console.log(err);
});
}
It happens the same to me, but i managed to replace the whole history stack, in my case after a certain point i don't need to keep the history stack, this is how i did it:
this.navCtrl.setPages([
{ page: RootPageHere }
]);
Works inside of a promise then function.
This one's killing me. I've read through a lot of Oleg's comments, and through the documentation, but I think I'm overlooking something really simple.
I have a jqGrid populated by calling a webmethod that returns JSON. We're good there. I'm using the Navigator for my "Add" button, and using onSelectRow w/ jqGrid.editRow() for my editing.
I get the dialog box when clicking the "Add" button, and can fill everything in. However, I get a error Status: 'Internal Server Error'. Error code: 500 return message after clicking the Submit button. Using Firebug, the Response is {"Message":"Invalid JSON primitive: FileType.","StackTrace":..... and the Post is FileType=3&ExportDate=12%2F29%2F2010&oper=add&id=_empty.
Obviously, my post is not getting "jsonified". I have tried using serializeEditData, and beforeSubmit in an attempt to manually return JSON.stringify(eparams);, but nothing has worked. Please see my code below.
Webmethod
<WebMethod()> _
<ScriptMethod()> _
Public Sub ModifyFileLog(ByVal FileType As String, _
ByVal ExportDate As Nullable(Of Date), _
ByVal oper As String, ByVal id As String)
Try
' blah
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub
JS - Globals
jQuery.extend(
jQuery.jgrid.defaults, {
type: "POST",
mtype: "POST",
datatype: "json",
ajaxGridOptions: { contentType: "application/json" },
ajaxRowOptions: { contentType: "application/json" },
rowNum: 10,
rowList: [10, 20, 30],
serializeGridData: function(data) {
return JSON.stringify(data);
},
gridview: true,
viewrecords: true,
sortorder: "asc"
},
jQuery.jgrid.edit, {
ajaxEditOptions: { contentType: "application/json" },
recreateForm: true,
serializeEditData: function(postData) {
return JSON.stringify(postData);
}
}
);
JS - jqGrid
var tblName = "tblFiles";
var pager1 = '#pagerFiles';
var grid = $("#" + tblName);
grid.jqGrid({
url: 'WebService.asmx/GetFileLog',
colNames: ['ID', 'File Type', 'Report Date', 'Export Date', 'EE Count'],
ajaxGridOptions: {
success: function(data, textStatus) {
if (textStatus == "success") {
ReceivedClientData(JSON.parse(getMain(data)).rows, grid); // populates grid
endGridRequest(tblName); // hides the loading panel
}
},
error: function(data, textStatus) {
alert(textStatus);
alert('An error has occured retrieving data!');
}
},
editurl: "WebService.asmx/ModifyFileLog",
serializeEditData: function(postData) {
return JSON.stringify(postData);
},
recreateForm: true,
pager: pager1,
...
}); // end .jqGrid()
grid.jqGrid('navGrid', pager1, { add: true, del: false, edit: true, view: false, refresh: true, search: false },
{}, // use default settings for edit
{
//beforeSubmit: submitAddFileLog,
closeAfterAdd: false,
closeAfterEdit: true
}, // use default settings for add
{}, // delete instead that del:false we need this
{multipleSearch: false }, // enable the advanced searching
{closeOnEscape: true} /* allow the view dialog to be closed when user press ESC key*/
); // end grid/jqGrid('navGrid')
NOTE: I started out populating by using $.ajax() by way of datatype: function(data), but thought I would return to the simplest example to get this to work. If you care to offer your thoughts on the advantages of using $.ajax() over simply using grid.jqGrid({ url: blah });, I'd love to learn more. Otherwise, please let me know if it would be more appropriate to post it as a separate question.
Also, please let me know if I'm just flat out doing this the wrong way. I'm not locked in to any one way of getting this done. I would prefer to be wrong and to learn how to do this the right way, than to be "right" in my own mind and continue to hack my way through it.
Any help, along w/ examples, would be hugely appreciated.
In my opinion your main problem is in JS - Globals. You use jQuery.extend function in a wrong way. You should replace one call
jQuery.extend(
jQuery.jgrid.defaults, {
// ...
},
jQuery.jgrid.edit, {
// ...
}
);
to two separate calls:
jQuery.extend(
jQuery.jgrid.defaults, {
// ...
}
);
jQuery.extend(
jQuery.jgrid.edit, {
// ...
}
);
After that the edit request to the server will be {"FileType":3,"ExportDate"="12/29/2010","oper":"add","id":"_empty"} instead of FileType=3&ExportDate=12%2F29%2F2010&oper=add&id=_empty.
Next, I am not sure that you can use ExportDate as a Date (DateTime ???) type. Probably you should start with String type and then convert the input data to the datatype which you need.
Next remark. Be sure that ModifyFileLog return JSON data. For example you can use <ScriptMethod(ResponseFormat:=ResponseFormat.Xml)> instead of <ScriptMethod()>. If you use .NET 4.0 you can achieve the same in many other ways.
One more thing. The ModifyFileLog should be Function instead of Sub and return the Id of new added object. In case of edit or del operations the return value will be ignored.
Because ModifyFileLog Function will be returned JSON data you have to decode/parse it. You can do this almost in the same way which I described here. In case of ASMX web service you can do about following:
jQuery.extend(
jQuery.jgrid.edit, {
ajaxEditOptions: { contentType: "application/json" },
recreateForm: true,
serializeEditData: function(postData) {
return JSON.stringify(postData);
},
afterSubmit: function (response, postdata) {
var res = jQuery.parseJSON(response.responseText);
return [true, "", res.d];
}
}
);