I have the following code :
ngOnInit() {
this.data = this.apollo.query({ query: ResidentQuery }).subscribe(({data, loading}) => {
this.data = data;
this.loading = loading;
});
if (!this.loading) {
// using this.data
}
}
I want data to be loaded before processed themm after the (!this.loading). It is not the case as loading is asynchronous. How I can wait the data is loaded before using them ?
I am making a graphql query using apollo client. ResidentQuery is a string containing the graphql query.
Thank you for your feedbacks !
I'm not sure how angular works, but I know how it works in react and I know the two are similar.
In react, you need to use componentWillReceiveProps() (EDIT: Now Deprecated), for example:
componentWillReceiveProps(newProps){
if(!newProps.query.loading){
this.setState({data: newProps.query.data})
}
}
From the medium article for how angular and react lifecycles are similar
Basically, the props will be bind automatically by Angular, so I
suppose the setter and getter function will be that part, which gives
you a way to generate complex state or variable, like what you can do
in componentWillReceiveProps().
Related
My type-ahead search was working great with REST but I'm converting to GraphQL, which has its challenges.
As the user types a last name into a form field the suggested results display in a data table below. Each letter is handled by the RxJS Subject.
The var searchTerm$ is a type of RXJS observable called a Subject binds to the HTML. The following is called from the OnViewInit lifecycle hook in an Angular app. The search is by the database column last_name.
However, this results in a Bad Request 400 error as the view loads and search doesn't work. I thought maybe this calls for a subscription but everything I find on those is about using web sockets to connect to a remote URL and server. Where do I go from here?
I'm using the Angular Apollo client with Apollo Express but I would be happy with any JS solution and try to figure it out from there. The server side is Nestjs which just wraps Apollo Server.
const lastNameSearch = gql `
query ($input: String!) {
lastNameSearch(input: $input) {
first_name
last_name
user_name
pitch
main_skill_title
skills_comments
member_status
}
}`;
this.apollo
.watchQuery({
query: lastNameSearch,
variables: {
last_name: searchTerm$, // Trying to use the observable here.
},
})
.valueChanges
.subscribe(result => {
console.log('data in lastNameSearch: ', result);
}),
The schema on the server:
lastNameSearch(input: String!): [Member]
The resolver:
#Query()
async lastNameSearch(#Args('input') input: String) {
const response = await this.membersService.lastNameSearch(input);
return await response;
}
Edit:
The error from the Network panel in dev tools. Console message worthless.
{"errors":[{"message":"Variable \"$input\" of required type \"String!\" was not provided.","locations":[{"line":1,"column":8}],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["GraphQLError: Variable \"$input\" of required type \"String!\" was not provided."," at getVariableValues
And this goes on showing properties and methods in the app for another 300 lines or so.
First, a big thank you to the amazing Daniel Rearden for his help on various questions as I and lots of others on SO learn GraphQL! He has patience!
As Daniel pointed out in comments I had a simple mistake. I'll point it out in the commented code below. However, the big issue was trying to use an observable, subject, or similar method as a variable. Even if the RxJS subject is emitting a string GraphQL will hate trying to use a large object as a var. So I had to use a little reactive programming to solve this.
Setup the observable:
public searchTerm$ = new Subject<string>(); // Binds to the html text box element.
Second, let's set this up in a lifecycle hook where we subscribe to the observable so it will emit letters one at a time as they are typed into an input box.
ngAfterViewInit() {
let nextLetter: string;
// -------- For Last Name Incremental Query --------- //
this.searchTerm$.subscribe(result => {
nextLetter = result; // Setup a normal variable.
this.queryLastName(nextLetter); // Call the GraphQL query below.
});
}
Last step we have the GraphQL query and consuming the returned data object. This works perfect to say type a 'p' into the form and get back from a db all the last names starting with 'p' or 'P'. Type 'r' and the results narrow to last names starting with 'pr', and so on.
private queryLastName(nextLetter) {
const lastNameSearch = gql`
query ($input: String!) {
lastNameSearch(input: $input) {
first_name
last_name
user_name
pitch
main_skill_title
skills_comments
member_status
}
}`;
this.apollo
.watchQuery({
query: lastNameSearch,
variables: {
input: nextLetter, // Notice I had used last_name here instead of input.
},
})
.valueChanges
.subscribe(result => {
// Put the data into some UI in your app, in this case
// an Angular Material data table.
// Notice how we get the data from the returning object.
// The avoids the dreaded "null" error when the shape of the
// returned data doesn't match the query. This put an array
// of objects into the UI.
this.dataSource.data = result.data['lastNameSearch'];
},
);
}
I'm trying to figure out how to mock requests when the client is ahead of the server. I'd like to be able to just request literals so that I can go back and change them later, is there a way to do something like this?
query myQuery {
type {
fieldName: 42
}
}
Yes, it is quite easy to set up mock responses if you have at least the server boilerplate code set up. If you are using Apollo, there are built in tools to facilitate mocks.
https://www.apollographql.com/docs/graphql-tools/mocking.html
From the docs:
The strongly-typed nature of a GraphQL API lends itself extremely well
to mocking. This is an important part of a GraphQL-First development
process, because it enables frontend developers to build out UI
components and features without having to wait for a backend
implementation.
Here is an example from the docs:
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { graphql } from 'graphql';
// Fill this in with the schema string
const schemaString = `...`;
// Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString });
// Add mocks, modifies schema in place
addMockFunctionsToSchema({ schema });
const query = `
query tasksForUser {
user(id: 6) { id, name }
}
`;
graphql(schema, query).then((result) => console.log('Got result', result));
This mocking logic simply looks at your schema and makes sure to
return a string where your schema has a string, a number for a number,
etc. So you can already get the right shape of result. But if you want
to use the mocks to do sophisticated testing, you will likely want to
customize them to your particular data model.
I need some help using the new Query and Mutation component in Apollo 2.1, especially with multiple queries and mutations.
I have the following problems:
I have a graphql request that depends on a previous graphql result, how can I deal with this?
How do I add two different mutations (in my component I need to do two different actions) in a component that already has a query?
edit 2019/08/24
from the Apollo docs:
The new hooks API for Apollo Client is a simpler way to fetch data in
your React app without the boilerplate of render prop components and
higher-order components (HOC). We recommend using hooks for all new
Apollo code going forward.
original answer:
You are supposed to nest them. See this example:
const NumbersWithData = () => (
<Query query={QueryOne}>
{({ loading: loadingOne, data: { one } }) => (
<Query query={QueryTwo}>
{({ loading: loadingTwo, data: { two }}) => {
if (loadingOne || loadingTwo) return <span>loading...</span>
return <h3>{one} is less than {two}</h3>
}}
</Query>
)}
</Query>
);
To help with keeping the nesting manageable, you could check react-adopt. They have an Apollo ToDo App example, where they combine a Query and multiple Mutations.
For this purpose react-apollo exports a compose function. Using this function you may cleanly use several component enhancers at once. Including multiple graphql(), or even Redux connect() enhancers.
import { Mutation, compose, graphql } from "react-apollo";
class AddTweet extends Component {
....
....
....
}
export default compose(
graphql(GET_AUTHORS, { name: "getAuthors" }),
graphql(ADD_TWEET, { name: "addTweet" }),
connect(...), // incase you are using Redux
)(AddTweet);
An important note is that compose() executes the last enhancer first and works its way backwards through the list of enhancers.
One more thing lets say you were using this.props.data now you will get get undefined. just console.log(this.props) and you will see what is happening to props now. You will be having two properties now getAuthors and addTweet. So now it will be this.props.name-in-compose.name-of-type-in-typeDefs i.e. this.props.getAuthors.getUsers. It took me a bit to figure it out.
In my opinion,
To make a request depends on previous request, you can break that request to children component and pass result of previous request like props to it and do that request.
To use more than one mutation and queries, you can use compose like this
...
#compose(
graphql(GET_FEEDS_QUERY, {name : 'getFeeds'}),
graphql(CREATE_NEW_POST, {name: "createNewPost"}),
graphql(LIKE_POST_MUTATION, { name: "unlikePostMutation"}),
...
)
class HomeScreen extends Component {
...
}
I wrote a Medium Post about how to combine Mutation and Query on the same Component.
Here is a snippet from the post
// other import
import {Query} from “Apollo-graphql”; // new Query Component
import gql from "graphql-tag";
import { graphql } from "react-apollo";
import UserComponent from '../component/UserComponent'; // any component to display query result
const GET_ALL_USER = gql`
{
allUsers: {
firstname,
lastname,
username,
# other information
}
}
`
const UPDATE_USER_STATUS = gql`
mutation UpdateUserStatus($userID: ID!, $status: Int!){
updateUserState(userID: $userID, status: $status){
firstname,
lastname
username
# other information
}
}
`
ExampleComponent extends React.Component{
onEditInformation = async (user) => {
const response = await mutate({
variables: {userID: user}
})
}
render(){
return(
<Query query={GET_ALL_USER}>
{({data: { allUsers }}) => {
return allusers.map(user => {
return (
<UserComponent
user={user}
onEdit={() => this.onEditInformation(user)}
/>
)
})
}}
</Query>
)
}
}
export default graphql(UPDATE_USER_STATUS)(ExampleComponent);
Asides from using compose from react-apollo, another great utility library you can check it out is react-adopt. A great small utility lib that helps you to compose multiple render props type components so you don't have a nested hell patterns.
I have wrote a similar answer that basically covers all your current needs in terms of:
How to consume a previous result from your mapper fn via react-adopt
Combine multiple Query/Mutations from Composed component via react-adopt
Here's the detailed answer you're looking for & hopefully can be helpful to solving your problems :)
Best solution for this
Simply nest graphql function
export default graphql(addBookMutation)(graphql(getAuthorsQuery)(AddBook))
You can refer to this
Apollo concepts
So I am working on couple of cases in my app where I need the following to happen
When event triggered, do the following
List item
check if the data with that context is already cached, serve cached
if no cache, debounce 500ms
check if other http calls are running (for the same context) and kill them
make http call
On success cache and update/replace model data
Pretty much standard when it comes to typeahead functionality
I would like to use observables with this... in the way, I can cancel them if previous calls are running
any good tutorials on that? I was looking around, couldn't find anything remotely up to date
OK, to give you some clue what I did now:
onChartSelection(chart: any){
let date1:any, date2:any;
try{
date1 = Math.round(chart.xAxis[0].min);
date2 = Math.round(chart.xAxis[0].max);
let data = this.tableService.getCachedChartData(this.currentTable, date1, date2);
if(data){
this.table.data = data;
}else{
if(this.chartTableRes){
this.chartTableRes.unsubscribe();
}
this.chartTableRes = this.tableService.getChartTable(this.currentTable, date1, date2)
.subscribe(
data => {
console.log(data);
this.table.data = data;
this.chartTableRes = null;
},
error => {
console.log(error);
}
);
}
}catch(e){
throw e;
}
}
Missing debounce here
-- I ended up implementing lodash's debounce
import {debounce} from 'lodash';
...
onChartSelectionDebaunced: Function;
constructor(...){
...
this.onChartSelectionDebaunced = debounce(this.onChartSelection, 200);
}
For debaunce you can use Underscore.js. The function will look this way:
onChartSelection: Function = _.debounce((chart: any) => {
...
});
Regarding the cancelation of Observable, it is better to use Observable method share. In your case you should change the method getChartTable in your tableService by adding .share() to your Observable that you return.
This way there will be only one call done to the server even if you subscribe to it multiple times (without this every new subscription will invoke new call).
Take a look at: What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?
I want to make requests to two different APIs. I then need to organize that data. I'm using redux-promise.
Currently, I have a function, calls two other functions that do the AJAX request:
export function fetchEstimates(input) {
let firstRequest = fetchFirstRequest(input);
let secondRequest = fetchFirstRequest(input);
return {
type: FETCH_DATA,
payload: {
firstRequest: firstRequest
secondRequest: secondRequest
}
}
}
Unfortunately, by putting both requests in an object, I can't seem to access the results.
export default function(state = [], action) {
switch (action.type) {
case FETCH_DATA:
// console.log(action.firstRequest);
// console.log(action.secondRequest);
return result;
}
return state;
}
As I toggle the object in dev tools, I come to this:
[[PromiseStatus]]:"resolved"
[[PromiseValue]]:Object
I can continue to toggle the options, but I can't seem to access them in my code.
If in my payload, I just return this
payload: firstRequest
I don't have issues. But of course, I need both requests. Any ideas. What is a good approach to handle this?
If you look at the source for redux-promise, you'll see that it assumes you've provided a promise as the "payload" field in your action. You're instead providing an object with two promises as sub-fields under "payload". I'm assuming that you're really interested in having both promises resolve, and then passing both results to the reducer. You'd want to use Promise.all to create a new promise that receives the results of both original promises as an argument, then use that promise as your payload. The reducer would then receive something like: {type : "DATA_RECEIVED", payload : [response1, response2]}.
You need some sort of middleware to deal with Promises (like redux-thunk), and wrap the promises in Promise.all to wait until they're both resolved. Let me know if you need a code example.