I am working on a project where I have completed my website through React JS.
Now I am working on developing App using flutter. All backend are done using aws amplify.
While working on flutter I am facing strange issue. When I am querying the aws Table in flutter I am getting empty list, as there are data on that table, query same table on React works and working till now.
So I planned to create some test file to check what the issue so I create test flutter project and test react project .
I will add the code down below
. After doing the test I came to know that, data I save from flutter can be only queried using flutter and data saved in react can only queried through react as it is same table, same region ,every thing is same. Why is this strange behaviour .
As you can see query same table but results are different and flutter created data is not showing up in react app and even here
Code of flutter are as below:
Main.dart :
import 'package:amplify_datastore/amplify_datastore.dart';
import 'package:amplify_flutter/amplify.dart';
import 'package:flutter/material.dart';
import 'package:my_app/amplifyconfiguration.dart';
import 'models/ModelProvider.dart';
import 'screens/homepage.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await configureAmplify();
runApp(MyApp());
}
Future<void> configureAmplify() async {
Amplify.addPlugin(AmplifyDataStore(modelProvider: ModelProvider.instance));
try {
await Amplify.configure(amplifyconfig);
} catch (e) {
print("Amplify is alreay configured");
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
HomePage:
import 'package:amplify_flutter/amplify.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_app/models/Person.dart';
class HomePage extends StatelessWidget {
Future<void> initTodos() async {
final model = Person(name: "runzun");
await Amplify.DataStore.save(model);
final persons = await Amplify.DataStore.query(Person.classType);
print(persons);
print('======================');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello'),
),
body: Center(
child: GestureDetector(
onTap: () async {
await initTodos();
// var todos = Todo(name: "runzun", description: "this is a blog");
// await Amplify.DataStore.save(todos);
},
child: Text('Add todos')),
),
);
}
}
models:
ModelProvider.dart
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
// ignore_for_file: public_member_api_docs
import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart';
import 'Person.dart';
export 'Person.dart';
class ModelProvider implements ModelProviderInterface {
#override
String version = "115942e9588e46ca9df7bb771727f5c7";
#override
List<ModelSchema> modelSchemas = [Person.schema];
static final ModelProvider _instance = ModelProvider();
static ModelProvider get instance => _instance;
ModelType getModelTypeByModelName(String modelName) {
switch(modelName) {
case "Person": {
return Person.classType;
}
break;
default: {
throw Exception("Failed to find model in model provider for model name: " + modelName);
}
}
}
}
Person.dart:
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
// ignore_for_file: public_member_api_docs
import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart';
import 'package:flutter/foundation.dart';
/** This is an auto generated class representing the Person type in your schema. */
#immutable
class Person extends Model {
static const classType = const _PersonModelType();
final String id;
final String? _name;
final String? _des;
#override
getInstanceType() => classType;
#override
String getId() {
return id;
}
String get name {
try {
return _name!;
} catch(e) {
throw new DataStoreException(DataStoreExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, recoverySuggestion: DataStoreExceptionMessages.codeGenRequiredFieldForceCastRecoverySuggestion, underlyingException: e.toString());
}
}
String? get des {
return _des;
}
const Person._internal({required this.id, required name, des}): _name = name, _des = des;
factory Person({String? id, required String name, String? des}) {
return Person._internal(
id: id == null ? UUID.getUUID() : id,
name: name,
des: des);
}
bool equals(Object other) {
return this == other;
}
#override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is Person &&
id == other.id &&
_name == other._name &&
_des == other._des;
}
#override
int get hashCode => toString().hashCode;
#override
String toString() {
var buffer = new StringBuffer();
buffer.write("Person {");
buffer.write("id=" + "$id" + ", ");
buffer.write("name=" + "$_name" + ", ");
buffer.write("des=" + "$_des");
buffer.write("}");
return buffer.toString();
}
Person copyWith({String? id, String? name, String? des}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
des: des ?? this.des);
}
Person.fromJson(Map<String, dynamic> json)
: id = json['id'],
_name = json['name'],
_des = json['des'];
Map<String, dynamic> toJson() => {
'id': id, 'name': _name, 'des': _des
};
static final QueryField ID = QueryField(fieldName: "person.id");
static final QueryField NAME = QueryField(fieldName: "name");
static final QueryField DES = QueryField(fieldName: "des");
static var schema = Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) {
modelSchemaDefinition.name = "Person";
modelSchemaDefinition.pluralName = "People";
modelSchemaDefinition.addField(ModelFieldDefinition.id());
modelSchemaDefinition.addField(ModelFieldDefinition.field(
key: Person.NAME,
isRequired: true,
ofType: ModelFieldType(ModelFieldTypeEnum.string)
));
modelSchemaDefinition.addField(ModelFieldDefinition.field(
key: Person.DES,
isRequired: false,
ofType: ModelFieldType(ModelFieldTypeEnum.string)
));
});
}
class _PersonModelType extends ModelType<Person> {
const _PersonModelType();
#override
Person fromJson(Map<String, dynamic> jsonData) {
return Person.fromJson(jsonData);
}
}
backend schema.graphql:
type Person #model {
id: ID!
name: String!
des: String
}
React JS app:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Amplify from 'aws-amplify';
import aws_exports from './../src/aws-exports';
Amplify.configure(aws_exports);
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
reportWebVitals();
app.js:
import logo from './logo.svg';
import './App.css';
import { useEffect } from 'react';
import { API, graphqlOperation } from 'aws-amplify';
import { listPeople } from './graphql/queries';
import { createPerson } from './graphql/mutations';
function App() {
useEffect(() => {
const test = async () => {
try {
await API.graphql(
graphqlOperation(createPerson, {
input: {
name: 'runzun Node',
},
})
);
const result = await API.graphql(graphqlOperation(listPeople));
console.log(result);
} catch (err) {
console.log(err);
}
// const peopleList = result.data.listPeople;
};
test();
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
graphql Query :
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const getPerson = /* GraphQL */ `
query GetPerson($id: ID!) {
getPerson(id: $id) {
id
name
des
createdAt
updatedAt
}
}
`;
export const listPeople = /* GraphQL */ `
query ListPeople(
$filter: ModelPersonFilterInput
$limit: Int
$nextToken: String
) {
listPeople(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
des
createdAt
updatedAt
}
nextToken
}
}
`;
graphql Mutation:
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const createPerson = /* GraphQL */ `
mutation CreatePerson(
$input: CreatePersonInput!
$condition: ModelPersonConditionInput
) {
createPerson(input: $input, condition: $condition) {
id
name
des
createdAt
updatedAt
}
}
`;
export const updatePerson = /* GraphQL */ `
mutation UpdatePerson(
$input: UpdatePersonInput!
$condition: ModelPersonConditionInput
) {
updatePerson(input: $input, condition: $condition) {
id
name
des
createdAt
updatedAt
}
}
`;
export const deletePerson = /* GraphQL */ `
mutation DeletePerson(
$input: DeletePersonInput!
$condition: ModelPersonConditionInput
) {
deletePerson(input: $input, condition: $condition) {
id
name
des
createdAt
updatedAt
}
}
`;
backend schema.graphql
type Person #model {
id: ID!
name: String!
des: String
}
Amplify Datastore is an offline first datastore. And it uses sqlite on flutter. what you need to do is clearly connect to your amplify studio by adding another plugin to your amplify config.
Add the api plugin to flutter
flutter pub add amplify_api
and change the init config so that,
this:
Amplify.addPlugin(AmplifyDataStore(modelProvider: ModelProvider.instance));
try {
await Amplify.configure(amplifyconfig);
} catch (e) {
print("Amplify is alreay configured");
}
becomes this:
final store = Amplify.addPlugin(AmplifyDataStore(modelProvider: ModelProvider.instance));
final api = AmplifyAPI();
await Amplify.addPlugins([api, store]);
try {
await Amplify.configure(amplifyconfig);
} catch (e) {
print("Amplify is alreay configured");
}
Related
I have built a web part that is similar to this web part from GitHub # https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-enhanced-list-formatting. The web part allows embedding custom CSS code inside SharePoint modern pages. But is it possible to modify the web part, to reference a CSS file instead of having to write the actual CSS code inside the web part? So instead of writing the CSS code as follow:-
to reference the css file as follow:-
Here is the EnhancedListFormattingWebPart.ts:-
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '#microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
} from '#microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '#microsoft/sp-webpart-base';
import * as strings from 'EnhancedListFormattingWebPartStrings';
import EnhancedListFormatting from './components/EnhancedListFormatting';
import { IEnhancedListFormattingProps } from './components/IEnhancedListFormattingProps';
import { PropertyPaneWebPartInformation } from '#pnp/spfx-property-controls/lib/PropertyPaneWebPartInformation';
import { PropertyFieldMonacoEditor } from '../../controls/PropertyFieldMonacoEditor';
export interface IEnhancedListFormattingWebPartProps {
css: string;
acceptedDisclaimer?: boolean;
}
export default class EnhancedListFormattingWebPart extends BaseClientSideWebPart <IEnhancedListFormattingWebPartProps> {
public render(): void {
const element: React.ReactElement<IEnhancedListFormattingProps> = React.createElement(
EnhancedListFormatting,
{
css: this.properties.css,
acceptedDisclaimer: this.properties.acceptedDisclaimer,
displayMode: this.displayMode,
context: this.context,
onAcceptDisclaimer: ()=>this._handleAcceptDisclaimer()
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
// protected get dataVersion(): Version {
// return Version.parse('1.0');
// }
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyFieldMonacoEditor('css', {
label: strings.CSSFieldLabel,
key: "cssText",
value: this.properties.css,
defaultValue: this.properties.css,
language: "css",
theme: "vs-light",
showLineNumbers: false,
onPropertyChange: (_propertyPath: string, _oldValue: string, value: string) => this._handleSave(value),
}),
PropertyPaneWebPartInformation({
description: strings.CSSDisclaimer,
key: 'cssDisclaimer'
})
]
}
]
}
]
};
}
private _handleAcceptDisclaimer = () => {
this.properties.acceptedDisclaimer = true;
this.render();
}
private _handleSave = (value: string) => {
this.properties.css = value;
}
}
here is the EnhancedListFormatting.tsx:-
import * as React from 'react';
import styles from './EnhancedListFormatting.module.scss';
import * as strings from 'EnhancedListFormattingWebPartStrings';
import { IEnhancedListFormattingProps } from './IEnhancedListFormattingProps';
import { MessageBarButton, MessageBar, MessageBarType } from 'office-ui-fabric-react';
import { DisplayMode } from '#microsoft/sp-core-library';
export default class EnhancedListFormatting extends React.Component<IEnhancedListFormattingProps, {}> {
public render(): React.ReactElement<IEnhancedListFormattingProps> {
const { css, displayMode, acceptedDisclaimer } = this.props;
// If we accepted the disclaimer, let's work as expected
// Determine if there is a CSS value
const hasCSS: boolean = css !== undefined && css !== "";
// Create a style element
const styleElem: JSX.Element = <style type="text/css">{css}</style>;
// If we're not in Edit mode, hide this web part
if (displayMode !== DisplayMode.Edit) {
return styleElem;
}
// if we didn't accept the disclaimer, show a disclaimer and nothing else
if (acceptedDisclaimer !== true) {
return (<MessageBar
onDismiss={()=>this._onAcceptDisclaimer()}
dismissButtonAriaLabel={strings.DismissDisclaimerAriaLabel}
messageBarType={MessageBarType.warning}
actions={
<div>
<MessageBarButton onClick={()=>this._onAcceptDisclaimer()}>{strings.AcceptDisclaimerButton}</MessageBarButton>
</div>
}
>
<div className={styles.disclaimerText} dangerouslySetInnerHTML={{__html: strings.DisclaimerText}}></div>
</MessageBar>);
}
return (
<div className={styles.enhancedListFormatting}>
{styleElem}
<MessageBar
messageBarType={hasCSS ? MessageBarType.success : null}
isMultiline={false}
actions={
<div>
<MessageBarButton onClick={() => this._onConfigure()}>{hasCSS ? strings.PlaceholderButtonTitleHasStyles : strings.PlaceholderButtonTitleNoStyles}</MessageBarButton>
</div>
}
>
{hasCSS ? strings.PlaceholderDescriptionHasStyles : strings.PlaceholderDescriptionNoStyles}
</MessageBar>
</div>
);
}
private _onAcceptDisclaimer() {
this.props.onAcceptDisclaimer();
}
private _onConfigure() {
this.props.context.propertyPane.open();
}
}
any idea, how i can achieve this? I want to do so , as we are planing to create a lot of pages which have the custom css. And instead of having to modify all those pages in the future to change their css, we will only need to change the related css file, and the change will be applied automatically to all the pages at once.
Thanks
There is built-in support for theming in SharePoint. It includes both custom CSS support, custom color schema support, etc. You can start over here:
https://learn.microsoft.com/en-us/sharepoint/dev/general-development/themes-overview-for-sharepoint
The idea of modern web parts and removal of the "script editor" is to prohibit users from adding links to some random sources, so only secure approved things are allowed.
I'm building a JHipster Monolithic application with React. In the Backend, I can use the application.yml / ApplicationProperties.java to add properties (such as API keys) which may change between environments (Dev, Prod etc).
My question is, can I do the same thing on the React front-end? This is a Spring application so the same application.yml and ApplicationProperties.java are in place. Does anyone have a code example of surfacing custom properties to the UI?
I have read the answer from this post (JHipster React Front End (Gateway) Application Properties) but it did not help me because it is a monolithic application in my case.
The solution from the other post works for a monolith. For a full solution, see below:
First, make sure you have configured the config you want exposed. Also configure it in ApplicationProperties.java (or set ignoreUnknownFields to false):
application:
my-value: 'TestValue'
Create a REST endpoint for exposing the config value to the client (modified from the other answer):
package com.mycompany.myapp.web.rest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Resource to return custom config
*/
#RestController
#RequestMapping("/api")
public class CustomConfigResource {
#Value("${application.my-value:}")
private String myValue;
#GetMapping("/custom-config")
public CustomConfigVM getCustomConfig() {
return new CustomConfigVM(myValue);
}
class CustomConfigVM {
private String myValue;
CustomConfigVM(String myValue) {
this.myValue = myValue;
}
public String getMyValue() {
return myValue;
}
public void setMyValue(String myValue) {
this.myValue = myValue;
}
}
}
Create a reducer to fetch and store the information:
import axios from 'axios';
import { SUCCESS } from 'app/shared/reducers/action-type.util';
export const ACTION_TYPES = {
GET_CONFIG: 'customConfig/GET_CONFIG',
};
const initialState = {
myValue: '',
};
export type CustomConfigState = Readonly<typeof initialState>;
export default (state: CustomConfigState = initialState, action): CustomConfigState => {
switch (action.type) {
case SUCCESS(ACTION_TYPES.GET_CONFIG): {
const { data } = action.payload;
return {
...state,
myValue: data['myValue'],
};
}
default:
return state;
}
};
export const getConfig = () => ({
type: ACTION_TYPES.GET_CONFIG,
payload: axios.get('api/custom-config'),
});
Finally, call the reducer's getConfig method from a component, for example in App.tsx:
import { getConfig } from 'app/shared/reducers/custom-config';
...
useEffect(() => {
props.getConfig();
}, []);
...
const mapDispatchToProps = { getConfig };
...
So I think this issue comes from me not quite understanding the relationship between AWS cognito user pools and the auth rules in a graphql schema.
When I run the code below, I get the message "Not Authorized to access createUser on type User".
import React from 'react';
import { Auth, API, graphqlOperation } from 'aws-amplify';
import { withAuthenticator } from "#aws-amplify/ui-react";
// This was created automatically from the schema by aws amplify
const CreateUser = /* GraphQL */ `
mutation CreateUser(
$input: CreateUserInput!
$condition: ModelUserConditionInput
) {
createUser(input: $input, condition: $condition) {
id
username
conversations {
items {
id
convoLinkUserId
convoLinkConversationId
createdAt
updatedAt
}
nextToken
}
messages {
items {
id
authorId
content
messageConversationId
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
}
`;
async function signIn(username, password) {
try {
const user = await Auth.signIn(username, password);
const { attributes } = user;
console.log("User", attributes)
return user
} catch (error) {
console.log('error signing in', error);
}
}
async function createUser(id) {
// creating a new user in the dynamodb table
try {
const newUser = {input: {username: id, id}}
console.log("Creating new user", newUser)
await API.graphql(graphqlOperation(CreateUser, newUser))
} catch (err) {
console.log('Error creating user! :', err)
}
}
async function testApiCalls() {
await signIn("test#test.com", "notarealpassword123") // runs successfully
await createUser("test#test.com") // where the error happens
}
function App() {
testApiCalls()
return (
<div className="App">
Hello
</div>
);
}
export default withAuthenticator(App);
Other relevant code would be my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Amplify, { Auth } from 'aws-amplify';
import AWSAppSyncClient from 'aws-appsync'
import aws_config from './aws-exports';
import { ApolloProvider } from '#apollo/client';
Amplify.configure(aws_config);
aws_config.graphql_headers = async () => { const currentSession = await Auth.currentSession(); return { Authorization: currentSession.getIdToken().getJwtToken() }; };
const client = new AWSAppSyncClient({
url: aws_config.aws_appsync_graphqlEndpoint,
region: aws_config.aws_appsync_region,
auth: {
type: aws_config.aws_appsync_authenticationType, // AMAZON_COGNITO_USER_POOLS
jwtToken: async () => (await Auth.currentSession()).idToken.jwtToken
}
});
const WithProvider = () => (
<ApolloProvider client={client}>
<App/>
</ApolloProvider>
)
ReactDOM.render(
<WithProvider/>,
document.getElementById('root')
);
And the schema definition for the User object:
type User
#model
#auth(rules: [{ allow: owner, ownerField: "id", queries: null }]) {
id: ID!
username: String!
conversations: [ConvoLink] #connection(name: "UserLinks")
messages: [Message] #connection(name: "UserMessages")
createdAt: String
updatedAt: String
}
Ultimately, I'm trying to make something similar to this example. I've tried reading the aws amplify docs but haven't been able to properly understand how the graphql operations are effected by the authentication.
I just spent several hours battling this same issue. For me, I had to specify the authMode on the graphql request.
Rather than doing something like this:
await API.graphql(graphqlOperation(createFamily, {input: family}))
I had to use this:
await API.graphql({
query: createFamily,
variables: {input: family},
authMode: 'AMAZON_COGNITO_USER_POOLS'
})
I did try the solution from user patwords. However, nothing I did on the schema was effective (including adding #aws_cognito_user_pools as indicated).
Unfortunately, the Amplify documentation does not do a good job documenting the process. I hope this helps someone else save a bit of time.
I'm pretty sure that the solution was adding #aws_cognito_user_pools to the schema definition for User. I also changed it to allow the owner to do whatever they want, but before they were unable to query.
type User
#model
#auth(rules: [{ allow: owner}])
#aws_cognito_user_pools {
id: ID!
username: String!
conversations: [ConvoLink] #connection(name: "UserLinks")
messages: [Message] #connection(name: "UserMessages")
createdAt: String
updatedAt: String
}
The problem is that the auth mode for the model does not match the configuration.
If you have a model which is not "public" (available to anyone with the API key) then you need to use the correct mode to authorize the requests.
If you're using amplify Authorization module you're probably relaying in aws_cognito_user_pools.
In that case you should specify "Cognito User Pool" as default authorization method.
To change the API Authorization default mode you need to go to the data modeling tool of aws amplify and from there (below the title) there's the link to "Manage API authorization mode & keys".
When using the "Cognito User Pool" as default authorization method you can use the API as usual for private methods correctly.
This also fixed the subscriptions for me.
I am trying to display my name and email registered in salesforce using Lightning Web Component.
I imported User.Name and User.Email but still I am getting error.
Could you tell me why this happens?
Thanks in advance.
Blockquote
[Line: 4, Col: 23] LWC1512: Missing resource value for #salesforce/Schema/User.Name
[Line: 5, Col: 23] LWC1512: Missing resource value for #salesforce/Schema/User.EMAIL
JS
import { LightningElement, wire, track, api } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import USERID_FIELD from '#salesforce/user/Id';
import NAME_FIELD from '#salesforce/Schema/User.Name';
import MAIL_FIELD from '#salesforce/Schema/User.Email';
const FIELDS = [
USERID_FIELD, NAME_FIELD, MAIL_FIELD
];
export default class JsSample extends LightningElement {
#api recordId;
#wire(getRecord, {'recordId': USERID_FIELD, fields: FIELDS})
record;
getName() {
return getFieldValue(this.record.data, NAME_FIELD);
}
getEMail() {
return getFieldValue(this.record.data, MAIL_FIELD);
}
#track inputText = '';
handleChange(event){
this.inputText = event.target.value;
}
/**
* 初期化処理
*/
connectedCallback(){
}
}
HTML
<template>
<div class="container">
UserInfo<br>
<div class="showProperties">
Name:{name}<br>
Mail:{eMail}
</div>
</div>
<div class="おまけ">
<label for="wireText">Input Text</label><input type="text" id="wireText" onchange={handleChange}/>
<lightning-formatted-text value={inputText}></lightning-formatted-text>
</div>
</template>
update:
I cannot show my name using this code...
import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import Id from '#salesforce/user/Id';
import NAME_FIELD from '#salesforce/schema/User.Name';
import MAIL_FIELD from '#salesforce/schema/User.EMail';
const FIELDS = "[NAME_FIELD, MAIL_FIELD]";
export default class JsSample extends LightningElement {
#api recordId;
userId = Id;
#wire(getRecord, {recordId: '$userId', fields: FIELDS})
user;
get name() {
return getFieldValue(this.user.data, NAME_FIELD);
}
get eMail() {
return getFieldValue(this.user.data, MAIL_FIELD);
}
// #track inputText = '';
// handleChange(event){
// this.inputText = event.target.value;
// }
// /**
// * init
// */
// connectedCallback(){
// }
}
Wire adaptors use lowercase camel case names, for instance salesforce and schema (apart from SObject and field names). Your references to the schema objects have incorrect case with the word Schema. They should be:
import NAME_FIELD from '#salesforce/schema/User.Name';
import MAIL_FIELD from '#salesforce/schema/User.Email';
I made that correction and then pushed to my scratch org and it compiled and saved correctly.
I was trying to implement a login with firebase using the project firebaseui-web-react.
I was following this example done by firebaseui-web-react people.
You can find the base file here but I will paste it on this question:
/**
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// React core.
import React from 'react';
import ReactDOM from 'react-dom';
// Firebase.
import firebase from 'firebase/app';
import 'firebase/auth';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
// Styles
import styles from './app.css'; // This uses CSS modules.
import './firebaseui-styling.global.css'; // Import globally.
// Get the Firebase config from the auto generated file.
const firebaseConfig = require('./firebase-config.json').result;
// Instantiate a Firebase app.
const firebaseApp = firebase.initializeApp(firebaseConfig);
/**
* The Splash Page containing the login UI.
*/
class App extends React.Component {
uiConfig = {
signInFlow: 'popup',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
],
callbacks: {
signInSuccessWithAuthResult: () => false,
},
};
state = {
isSignedIn: undefined,
};
/**
* #inheritDoc
*/
componentDidMount() {
this.unregisterAuthObserver = firebaseApp.auth().onAuthStateChanged((user) => {
this.setState({isSignedIn: !!user});
});
}
/**
* #inheritDoc
*/
componentWillUnmount() {
this.unregisterAuthObserver();
}
/**
* #inheritDoc
*/
render() {
return (
<div className={styles.container}>
<div className={styles.logo}>
<i className={styles.logoIcon + ' material-icons'}>photo</i> My App
</div>
<div className={styles.caption}>This is a cool demo app</div>
{this.state.isSignedIn !== undefined && !this.state.isSignedIn &&
<div>
<StyledFirebaseAuth className={styles.firebaseUi} uiConfig={this.uiConfig}
firebaseAuth={firebaseApp.auth()}/>
</div>
}
{this.state.isSignedIn &&
<div className={styles.signedIn}>
Hello {firebaseApp.auth().currentUser.displayName}. You are now signed In!
<a className={styles.button} onClick={() => firebaseApp.auth().signOut()}>Sign-out</a>
</div>
}
</div>
);
}
}
// Load the app in the browser.
ReactDOM.render(<App/>, document.getElementById('app'));
I implemented it using Typescript and it was working. Then I decided to refactor it because I wanted to keep everything regarding my login provider in a different class so that I could switch to a fake implementation for testing purposes.
So I implemented it this way:
LoginProvider.ts
export default interface LoginProvider {
unregister(): void
register(f:(user:any)=>void): void
actualProvider(): any
actualConfig(): any
}
FirebaseLoginProvider.ts
import LoginProvider from './LoginProvider';
export default class FirebaseLoginProvider implements LoginProvider {
private firebaseApp: any
private uiConfig: any
private unregisterAuthObserver: () => void
constructor(firebaseApp: any, uiConfig: any) {
this.uiConfig = uiConfig
this.firebaseApp = firebaseApp
}
public register(f:(user:any)=>void): void {
this.unregisterAuthObserver = this.firebaseApp.auth().onAuthStateChanged((user: any) => {
console.log("state changed " + !!user)
f(user)
}).bind(this)
}
public unregister(): void {
this.unregisterAuthObserver()
}
public actualProvider(): any {
return this.firebaseApp.auth()
}
public actualConfig(): any {
return this.uiConfig
}
}
Login.tsx
import 'firebase/auth';
import './firebase-global.css';
import * as React from 'react';
import { RouteComponentProps, withRouter } from "react-router-dom";
import LoginProvider from './LoginProvider';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
interface Props {
};
interface State {
isSignedIn: boolean
}
interface HomeProps extends RouteComponentProps<Props> {
loginProvider: LoginProvider
}
class Login extends React.Component<HomeProps, State> {
state = {
isSignedIn: false
}
constructor(props: HomeProps) {
super(props);
}
componentDidMount() {
this.props.loginProvider.register((user) => {
this.setState({ isSignedIn: !!user } as State);
})
}
componentWillUnmount() {
this.props.loginProvider.unregister();
}
render() {
const signedIn = this.state.isSignedIn
return (
<div className='container'>
{!signedIn &&
<div>
<StyledFirebaseAuth
className='firebaseUi'
uiConfig={this.props.loginProvider.actualConfig()}
firebaseAuth={this.props.loginProvider.actualProvider()} />
</div>
}
{signedIn && this.props.history.goBack()}
</div>
);
}
}
export default withRouter(Login);
My implementation is working properly but I'm getting this warning inside the browser console and I can't understand why.
Warning: Cannot update during an existing state transition (such as within 'render'). Render methods should be a pure function of props and state.
I also have the following stack trace:
React 8
listener createTransitionManager.js:46
notifyListeners createTransitionManager.js:65
notifyListeners createTransitionManager.js:64
setState createBrowserHistory.js:78
handlePop createBrowserHistory.js:103
confirmTransitionTo createTransitionManager.js:36
handlePop createBrowserHistory.js:101
handlePopState createBrowserHistory.js:85
go createBrowserHistory.js:214
goBack createBrowserHistory.js:218
render Login.tsx:55
React 13
componentDidMount Login.tsx:34
unregisterAuthObserver FirebaseLoginProvider.ts:16
next index.cjs.js:1303
sendOne index.cjs.js:1407
The point is that I didn't see the issue with the other implementation but I just extract things into methods and classes so I don't understand why it's giving me this error where there wasn't one before.
The issue seems to be with react-router where I try do:
{signedIn && this.props.history.goBack()}
But if I put this call inside componentDidMount, it will not be called after the firebase login page closes.
I'm probably missing something about the React lifecycle. Could you please help me solve the issue?
Ok I solved it.
I was not aware of componentDidUpdate.
This is the new implementation without the warning:
import 'firebase/auth';
import './firebase-global.css';
import * as React from 'react';
import { RouteComponentProps, withRouter } from "react-router-dom";
import LoginProvider from './LoginProvider';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
interface Props {
};
interface State {
isSignedIn: boolean
}
interface HomeProps extends RouteComponentProps<Props> {
loginProvider: LoginProvider
}
class Login extends React.Component<HomeProps, State> {
state = {
isSignedIn: false
}
constructor(props: HomeProps) {
super(props);
}
componentDidMount() {
this.props.loginProvider.register((user) => {
this.setState({ isSignedIn: !!user } as State);
})
}
componentDidUpdate() {
this.goBackWhenLoggedIn();
}
componentWillUnmount() {
this.props.loginProvider.unregister();
}
render() {
const signedIn = this.state.isSignedIn
return (
<div className='container'>
{!signedIn &&
<div>
<StyledFirebaseAuth
className='firebaseUi'
uiConfig={this.props.loginProvider.actualConfig()}
firebaseAuth={this.props.loginProvider.actualProvider()} />
</div>
}
</div>
);
}
private goBackWhenLoggedIn() {
if (this.state.isSignedIn) {
this.props.history.goBack()
}
}
}
export default withRouter(Login);