2016-08-24 4 views
0

Ich habe eine Relay.Mutation, dass Updates auf User Objekte ausgelöst werden soll:Wie mit ungelösten Requisiten in Relay-Mutationen umgehen?

class UserMutation extends Relay.Mutation { 
    public getMutation() { 
     return Relay.QL`mutation {saveUser}`; 
    } 

    public getVariables() { 
     return { 
      id: this.props.user.id, 
      loginName: this.props.user.loginName, 
      firstName: this.props.user.firstName, 
      lastName: this.props.user.lastName, 
      mail: this.props.user.mail 
     } 
    } 

    public getFatQuery() { 
     return Relay.QL` 
      fragment on UserPayload { 
       user { 
        id, 
        loginName, 
        firstName, 
        lastName, 
        mail 
       } 
      } 
     `; 
    } 

    public getConfigs() { 
     return [{ 
      type: "FIELDS_CHANGE", 
      fieldIDs: { 
       user: this.props.user.id 
      } 
     }]; 
    } 

    static fragments = { 
     user:() => Relay.QL` 
      fragment on User { 
       id, 
       // I initially had only id here 
       loginName, 
       firstName, 
       lastName, 
       mail 
      } 
     ` 
    } 
} 

ich diese Mutation in meiner Komponente UserDetails wie diese bin mit:

// I initially passed this.props.user to the mutation 
this.props.relay.commitUpdate(new UserMutation({ user: this.state.user }) 

Bei der Ausführung, Relais eine user zu den Pässen Backend mit nur id gesetzt, ohne irgendeine andere Eigenschaft. Die Mutation wird nicht ausgeführt, da die anderen Felder in der Eingabevariablen fehlen.

Nach dem Debuggen in die Mutation, sah ich, dass this.props.userundefined auf allen Feldern gesetzt hat, aber ID. this._unresolvedProps.user ist jedoch ein user mit allen Feldern richtig eingestellt.

Wenn ich den Code der Mutation ändern und alle this.props durch this._unresolvedProps ersetzen, werden alle notwendigen Daten an das Backend übertragen und die Mutation wird ohne Fehler ausgeführt. Der Frontend-Cache scheint ebenfalls korrekt aktualisiert zu sein (Felder wie firstName werden in anderen Komponenten aktualisiert). Aber ich erwarte nicht, dass dies der richtige Weg ist.

Was vermisse ich?

UPDATE

Die UserDetails Komponente lädt Benutzer Daten wie loginName und liefert Textfelder, diese Eigenschaften zu ändern. Die entsprechenden Relais Container sehen wie folgt aus:

export default Relay.createContainer(UserDetails, { 
    fragments: { 
     user:() => Relay.QL` 
      fragment on User { 
       id, 
       loginName, 
       firstName, 
       lastName, 
       mail, 
       roles { 
        id, 
        name 
       }, 
       ${UserMutation.getFragment("user")} 
      } 
     ` 
    } 
}); 

I Textfeld Änderungen in einem Texteingabehandler ...

public handleTextInput(fieldName: string, event: any) { 
    let user = this.state.user; 

    switch (fieldName) { 
     case "loginName": { 
      user.loginName = event.target.value; 
      break; 
     } 
     case "firstName": { 
      user.firstName = event.target.value; 
      break; 
     } 
     case "lastName": { 
      user.lastName = event.target.value; 
      break; 
     } 
     case "mail": { 
      user.mail = event.target.value; 
      break; 
     } 
    } 

    this.setState({ user: user }); 
} 

... behandeln und in einem einreichen Handler einreichen bilden, wo ich jetzt passieren this.state.user die Mutation:

public handleSubmit(e: any) { 
    e.preventDefault(); 
    this.props.relay.commitUpdate(new UserMutation({ user: this.state.user }), { 
     onSuccess: (response: any) => { 
      this.setState({ user: response.saveUser.user }); 
     } 
    }); 
} 

ich benutze einen C# Backend: graphql-dotnet. Das ist, was ich für die Mutation definiert:

public class ApplicationSchema : Schema 
{ 
    public ApplicationSchema() 
    { 
     this.Query = new ApplicationQuery(); 
     this.Mutation = new ApplicationMutation(); 
    } 
} 

public class ApplicationMutation : ObjectGraphType 
{ 
    public ApplicationMutation() 
    { 
     this.Name = "Mutation"; 

     // save a user 
     this.Field<UserPayloadType>(
      "saveUser", 
      arguments: new QueryArguments(
      new QueryArgument<NonNullGraphType<UserInputType>> 
      { 
       Name = "input", 
       Description = "the user that should be saved" 
      }), 
      resolve: context => 
       { 
        var userInput = context.Argument<UserInput>("input"); 
        var clientMutationId = userInput.ClientMutationId; 

        var user = MemoryRepository.UpdateUser(new User() 
        { 
         Id = userInput.Id, 
         LoginName = userInput.LoginName, 
         FirstName = userInput.FirstName, 
         LastName = userInput.LastName, 
         Mail = userInput.Mail 
        }); 

        return new UserPayload() 
        { 
         ClientMutationId = clientMutationId, 
         User = user 
        }; 
       }); 
    } 
} 

public class UserInputType : InputObjectGraphType 
{ 
    public UserInputType() 
    { 
     this.Name = "UserInput"; 

     this.Field<NonNullGraphType<StringGraphType>>("id", "The id of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("loginName", "The login name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("firstName", "The first name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("lastName", "The last name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("mail", "The mail adress of the user."); 

     this.Field<NonNullGraphType<StringGraphType>>("clientMutationId", "react-relay property."); 
    } 
} 

public class UserPayloadType : ObjectGraphType 
{ 
    public UserPayloadType() 
    { 
     this.Name = "UserPayload"; 

     this.Field<NonNullGraphType<UserType>>("user", "The user."); 

     this.Field<NonNullGraphType<StringGraphType>>("clientMutationId", "react-relay property."); 
    } 
} 

public class UserType : ObjectGraphType 
{ 
    public UserType() 
    { 
     this.Name = "User"; 
     this.Field<NonNullGraphType<StringGraphType>>("id", "The id of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("loginName", "The login name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("firstName", "The first name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("lastName", "The last name of the user."); 
     this.Field<NonNullGraphType<StringGraphType>>("mail", "The mail adress of the user."); 

     Field<ListGraphType<RoleType>>("roles", resolve: context => MemoryRepository.GetRolesOfUser(context.Source as DomainModel.Models.User)); 
    } 
} 
+0

Können Sie bitte Ihre serverseitige GraphQL Mutation zeigen 'saveUser'? Problem könnte mit 'saveUser' sein. –

+0

Fügen Sie auch das Feld "Fragmente" Ihres Relay-Containers hinzu. –

+0

Ich habe gerade die erforderlichen Codefragmente hinzugefügt. Vielen Dank. –

Antwort

2

Empfängt Ihr Relay-Container das User Fragment korrekt? Ich sehe in Ihrer static fragments Definition Fragment auf Benutzer ist nur ID-Feld, so frage ich mich, ob Ihre Eltern Relay-Komponente holt sie alle.

Da Ihre Mutation wirklich von diesen Feldern abhängig ist, fügen Sie sie zur fragments-Eigenschaft hinzu.

class UserMutation extends Relay.Mutation { 
    public getMutation() { ... } 

    // getVariables, FatQuery and Configs ... 

    static fragments = { 
     user:() => Relay.QL` 
      fragment on User { 
       id, 
       loginName, 
       firstName, 
       lastName, 
       mail 
      } 
     ` 
    } 
} 

Und dann versuchen, dieses Fragment in Relay-Komponente, die Ihre Mutation verwendet. Beispiel React-Relay-Komponente:

import UserMutation from 'mutations/user'; 

class User extends Component { 
    commit(e) { 
    Relay.Store.commitUpdate(
     new UserMutation({ 
     user: this.props.user 
     }) 
    ); 
    } 

    render() { 
    return (
     <div>Hello</div> 
    ); 
    } 
}; 

export default Relay.createContainer(User, { 
    fragments: { 
    user:() => Relay.QL` 
     fragment on User { 
     ${UserMutation.getFragment('user')} 
     } 
    `, 
    } 
}); 
+0

Re: Einschließen von Eigenschaften neben ID in "Fragmente" von Mutationen, Abhängigkeiten von Eigenschaften wie ID (z. B. in 'FIELDS_CHANGE' config) und Eigenschaften, die in optimistischer Antwort aktualisiert werden, müssen in' fragments' enthalten sein. Relay bestimmt anhand der Fettabfrage, welche Felder abgerufen werden sollen. Wenn in der Mutation keine optimistische Antwort definiert ist, müssen daher Felder mit Ausnahme der ID nicht in Fragmente eingeschlossen werden. –

+0

Danke, ich habe gerade den Code meines Elternrelay-Containers hinzugefügt. Ich habe auch alle Eigenschaften wie 'loginName' usw. zum Mutationsfragment hinzugefügt. Jetzt wird der Benutzer an das Backend gesendet, aber bei unveränderten Eigenschaften ignoriert er alle Texteingaben. Ich habe die relevanten Codefragmente in die Frage eingefügt. Ändere ich den Benutzer in der richtigen Weise? –

+0

Am Ende musste ich mein Schema anpassen, um mit Relais kompatibel zu sein. Ich benutze graphql-dotnet im Backend, wo ich einige meiner Diagrammtypen ändern musste (wie das Hinzufügen aller notwendigen Eigenschaften zur Benutzersammlung usw.). Vielen Dank für Ihre Unterstützung, denn die Auflistung aller Eigenschaften im Fragment war entscheidend. –

0

Verwenden REQUIRED_CHILDREN und Staat in der Komponente aktualisieren.

Anstatt FIELDS_CHANGE zu verwenden, können Sie REQUIRED_CHILDREN verwenden, mit dem Sie das zurückgegebene gespeicherte Objekt zu Ihrem Geschäft hinzufügen können. Was Sie tun würde, ist Ihre getConfigs wie folgt aufgebaut:

getConfigs() { 
    return [{ 
    type: 'REQUIRED_CHILDREN', 
     children: [ 
     Relay.QL` 
      fragment on UserPayload { 
      user { 
       id 
       loginName 
       firstName 
       lastName 
       mail 
      } 
      } 
     ` 
     ] 
    }] 
} 

Und ändern Ihre commitUpdate wie folgt aus:

this.props.relay.commitUpdate(
    new UserMutation({user: this.props.user}), 
    { 
    onSuccess: response => this.setState({ 
     user: response.user, 
    }), 
    onError: err => console.log(err) 
    } 
); 

Wie Sie sehen können, die onSuccess Rückruf können Sie einen actionCreator anrufen und setzen der neue Benutzer in den Zustand Ihrer Anwendung. Dies würden Sie mit der von Ihnen in Ihrer Anwendung verwendeten Statusverwaltung durchführen. In diesem Fall ist es einfach setState.

REQUIRED_CHILDREN config wird verwendet, um zusätzliche Kinder an die Mutationsabfrage anzuhängen. Sie müssen dies möglicherweise verwenden, um beispielsweise Felder auf einem neuen Objekt abzurufen, das durch die Mutation erstellt wurde (und das Relay normalerweise nicht abzurufen versucht, weil es zuvor noch nichts für dieses Objekt abgerufen hat).

Daten geholt als Ergebnis einer REQUIRED_CHILDREN Config wird nicht in den Client-Speicher geschrieben, aber Sie können Code hinzufügen, die es in der onSuccess Rückruf Prozesse, die Sie in commitUpdate passieren()

There is more information in the documentation about REQUIRED_CHILDREN here.

+0

Danke für diesen Hinweis. Aber mein tatsächliches Problem ist nicht, einen "Benutzer" zurückzubekommen, sondern einen "Benutzer" an die Mutation zu übergeben. Wenn ich in 'getVariables() 'debugge, sehe ich, dass' this.props.user' nur die 'id' gesetzt hat. Wenn ich also versuche, die Mutation ins Backend zu feuern, erhalte ich 'Variable '$ input_0' erwarteter Wert vom Typ 'UserInput', weil nur die' id' in der Eingabe gesetzt wird, aber nicht 'firstName',' lastName' etc. –

+0

Vielleicht setzen Sie Ihre Mutation auch auf die API? Problem könnte da sein. – alexi2

+0

Danke, ich habe gerade den relevanten Backend-Code für die Mutation hinzugefügt. –