Waiting for data to be retrieved from local storage - javascript

When the gallery is loaded in my gallery app, a functioned is called to check whether the current user has liked an image in an array of images. If the user has liked the image, a solid heart icon is displayed on top of the image. If the hasn't liked the image, a heart outline icon is displayed.
Within the function, we get the current users index in an array of users. This information is retrieved from a user object which is stored in the browsers local storage. The problem I am having is that the function is called before the user data is retrieved from the local storage and I'm not sure how to get around this problem?
Here is the relevant html from the gallery-list component:
<div class="gallery-container">
<div
class="image-container"
*ngFor="let image of galleryList">
<div *ngIf="checkLike(image.imagePath)" class="heart-icon-container">
<ion-icon name="heart" class="heart-icon" (click)="updateLike(image.imagePath)"></ion-icon>
</div>
<div *ngIf="!checkLike(image.imagePath)" class="heart-icon-container">
<ion-icon name="heart-outline" class="heart-icon" (click)="updateLike(image.imagePath)"></ion-icon>
</div>
</div>
</div>
Here is the relevant code from the gallery-list ts file:
currUsersIndex: number;
ngOnInit() {
this.currUsersIndex = this.usersService.getCurrUserArrIndex();
}
checkLike(imageUrl: string): boolean {
const users = this.usersService.getUsers();
if(this.isLoggedIn) {
const FavouritesList = users[this.currUsersIndex].likes;
if(FavouritesList.includes(imageUrl)) {
return true;
} else {
return false;
}
}
}
Finally, this is where the users index is retrieved from the users.service file:
private users: User[] = [];
getCurrUserArrIndex() {
const usersEmail = this.getCurrentUser().email;
const users = this.getUsers();
const usersIndex = users.findIndex(user => user.email === usersEmail);
return usersIndex;
}
getCurrentUser() {
const userData: {
email: string,
id: string,
_token: string,
_tokenExpirationDate: string
} = JSON.parse(localStorage.getItem('authUserData'));
return userData;
}
getUsers() {
return this.users.slice();
}
Please note, the users array is initially set to empty but it is set with the users from a firebase database when the app initializes.
The error message I am receiving in the console is: "can't access property "likes", users[this.currUsersIndex] is undefined"

I think, the issue here is that this.currUsersIndex is not set when you call checkLike() in the HTML.
Alternatively what i could recommend you is that, define a boolean variable in your component as,
checkLike = false;
and then call the function checkLike() after setting currUsersIndex
ngOnInit() {
this.currUsersIndex = this.usersService.getCurrUserArrIndex();
this.checkLike();
}
checkLike(imageUrl: string): boolean {
const users = this.usersService.getUsers();
if(this.isLoggedIn) {
const FavouritesList = users[this.currUsersIndex].likes;
if(FavouritesList.includes(imageUrl)) {
this.checkLike = true;
} else {
this.checkLike = false;
}
}
You must be already having the imageUrl in the component.ts just replace with that.

You don't need to separate things IMO. You can update your code to this, this will be error-proof.
checkLike(imageUrl: string): boolean {
const user = this.usersService.getCurrentUser();
if(user && user.likes) {
const FavouritesList = user.likes;
return FavouritesList.includes(imageUrl);
}
return false
}
getCurrentUser() {
const userData: {
email: string,
id: string,
_token: string,
_tokenExpirationDate: string
} = JSON.parse(localStorage.getItem('authUserData'));
const userEmail = userData.email;
const users = this.getUsers();
return users.find(user => user.email === usersEmail);
}

Related

React - How can I redirect to another page after some conditions are met on the OnClick of a Button?

So in this component, I have a button called Submit which when clicked will do the following operations: it will export the drawing that the user has made as a jpeg url, pass it to another api that returns an image caption, then compares this image caption to a string prompt that was returned prior. I would like to redirect the user to another webpage based on whether the comparison between the image caption and the prompt was the same or not. How would I go about this?
<button onClick={() => {
let W = this.handleWord()
let valid = this.state.valid
this.canvas.current.exportImage("jpeg")
.then(imagedata => {
fetch('https://hf.space/embed/Salesforce/BLIP/+/api/predict/', { method: "POST", body: JSON.stringify({"data":[imagedata,"Image Captioning","None","Beam Sampling"]}), headers: { "Content-Type": "application/json" } })
.then(function(response) {
return response.json(); })
.then(function(json_response)
{blip = json_response.data
valid = blip.includes(W) //blip which is a string returned by the api above is checked to see if W is in the string
console.log(valid)
//based on this valid boolean, i want to have page be redirected to either Success or Share, two seperate pages ive made
})
})
.catch(e => {
console.log(e);
});
}}
className='buttonprops'>
<img src={submitbutton} width = {120} height = {35} alt = 'Submit!' />
</button>
const YourComponent = () => {
// if you're using react-router...
const navigate = useNavigate(); // import { useNavigate } from 'react-router-dom'
const handleClick = () => {
// your button click logic...
navigate(valid ? "/success" : "/share")
// or if you're not using react-router...
window.location.pathname = valid ? "/success" : "/share"
}
return <button onClick={handleClick}>click me</button>
}

How to add Wrap resolver in NestJS and GraphQL to check if email from header is equal to the email in query

I'm using cognito authentication,
I create a middlware
const { email } = payload;
req.headers['user-email'] = email as string;
I want to write this kind of function
public async httpCheck(query: any, args: any, context: any,
resolveInfo: any) {
console.log('authhealth');
console.log("context "+ context.userEmail);
console.log("query : "+ query.userEmail);
(context.userEmail === query.userEmail ) ? console.log("authorized successfully") : console.log("authorization failed");
return 'OK';
}
This is my file structure, I want to write wrap resolver
From your example, it looks like you are wanting to reject the whole request if the email from the request header does not match an email being provided as an argument to a field in the GraphQL query.
So given the following query:
query MyQuery($userEmail:String!) {
userByEmail(email: $userEmail) {
id
email
familyName
givenName
}
}
If you want to check that the header email equals the email argument of userByEmail BEFORE Postgraphile executes the operation, you need to use a Postgraphile Server Plugin which adds a dynamic validation rule that implements the check:
import type { PostGraphilePlugin } from "postgraphile";
import type { ValidationRule } from "graphql";
import { GraphQLError } from "graphql";
import type { IncomingMessage } from "http";
import type { Plugin } from "graphile-build";
// Defines a graphile plugin that uses a field argument build hook to add
// metadata as an extension to the "email" argument of the "userByEmail" field
const AddEmailMatchPlugin: Plugin = (builder) => {
builder.hook(
"GraphQLObjectType:fields:field:args",
(args, build, context) => {
// access whatever data you need from the field context. The scope contains
// basically any information you might desire including the database metadata
// e.g table name, primary key.
const {
scope: { fieldName, isRootQuery },
} = context;
if (!isRootQuery && fieldName !== "userByEmail") {
return args;
}
if (args.email) {
return {
...args,
email: {
...args.email,
// add an extensions object to the email argument
// this will be accessible from the finalized GraphQLSchema object
extensions: {
// include any existing extension data
...args.email.extensions,
// this can be whatetever you want, but it's best to create
// an object using a consistent key for any
// GraphQL fields/types/args that you modify
myApp: {
matchToUserEmail: true,
},
},
},
};
}
return args;
}
);
};
// define the server plugin
const matchRequestorEmailWithEmailArgPlugin: PostGraphilePlugin = {
// this hook enables the addition of dynamic validation rules
// where we can access the underlying http request
"postgraphile:validationRules": (
rules,
context: { req: IncomingMessage; variables?: Record<string, unknown> }
) => {
const {
variables,
// get your custom user context/jwt/headers from the request object
// this example assumes you've done this in some upstream middleware
req: { reqUser },
} = context;
if (!reqUser) {
throw Error("No user found!");
}
const { email, role } = reqUser;
const vr: ValidationRule = (validationContext) => {
return {
Argument: {
// this fires when an argument node has been found in query AST
enter(node, key) {
if (typeof key === "number") {
// get the schema definition of the argument
const argDef = validationContext.getFieldDef()?.args[key];
if (argDef?.extensions?.myApp?.matchToUserEmail) {
// restrict check to a custom role
if (role === "standard") {
const nodeValueKind = node.value.kind;
let emailsMatch = false;
// basic case
if (nodeValueKind === "StringValue") {
if (node.value.value === email) {
emailsMatch = true;
}
}
// must account for the value being provided by a variable
else if (nodeValueKind === "Variable") {
const varName = node.value.name.value;
if (variables && variables[varName] === email) {
emailsMatch = true;
}
}
if (!emailsMatch) {
validationContext.reportError(
new GraphQLError(
`Field "${
validationContext.getFieldDef()?.name
}" argument "${
argDef.name
}" must match your user email.`,
node
)
);
}
}
}
}
},
},
};
};
return [...rules, vr];
},
// This hook appends the AddEmailMatchPlugin graphile plugin that
// this server plugin depends on for its custom extension.
"postgraphile:options": (options) => {
return {
...options,
appendPlugins: [...(options.appendPlugins || []), AddEmailMatchPlugin],
};
},
};
export default matchRequestorEmailWithEmailArgPlugin;
Then you need to register the server plugin in the Postgraphile middleware options:
const pluginHook = makePluginHook([MatchRequestorEmailWithEmailArgPlugin]);
const postGraphileMiddleware = postgraphile(databaseUrl, "my_schema", {
pluginHook,
// ...
});
If you just want to reject the userByEmail field in the query and don't care about rejecting before any resolution of any other parts of the request occur, you can use the makeWrapResolversPlugin to wrap the resolver and do the check there.

Angular typescript subscribing to a variable does not give correct results

Motive : I am doing a validation on book name during save - if there is already a book in the DB with the same name, validation error message should be thrown.
I have a method in my service that returns the books object if any books are present with the user entered book name.
After I call this service method I subscribe to it, assign the result to a bool variable and check the bool variable in if statement. since using subscribe/ observable, the if statement executes first and then the subscriber is getting called and the correct value is not returned and validation is not firing.
Where am I going wrong ? Thanks in advance.
Below is my code :
export class AddBooksComponent implements OnInit{
bookName = "";
isError = false;
errorMessage="";
isPresent:boolean = false;
constructor(public bsModalRef: BsModalRef,private booksService: BooksService) { }
ngOnInit(): void { }
saveBooks() {
if(this.checkBookExistenance()) {
this.isError = true
this.errorMessage="The provided book name is already exists."
} else {
this.bsModalRef.hide()
}
}
checkPreferenceExistenance():boolean {
this.booksService.getBookByName(bookNameName:trim(this.bookName))
.pipe(first())
.subscribe((data) => {
// if the response has data then the book is present
if(data.length) {
isPresent= true;
}
else{
isPresent= false;
}
});
return isPresent;
}
}
this.booksService.getBookByName() is asynchonous, it takes some time to execute, in the meantime the code procedes.
checkBookExistenanceshould return an observable:
checkPreferenceExistenance(): Observable<boolean> {
return this.booksService.getBookByName(bookNameName: trim(this.bookName)).pipe(
first(),
map(data => {
if (data.length){
return true;
}else{
return false;
}
})
// or shortform without if/else: map(data => data.length)
);
}
and then:
this.checkPreferenceExistenance().subscribe(
exist => {
if (exist) {
this.isError = true
this.errorMessage = "The provided book name is already exists."
} else {
this.bsModalRef.hide()
}
}
);

How to submit empty value and make it send None as a string react

I am trying to send an empty value and when I submit it, it send "None" to the data.
Here is my code base:
const [addLoanClause, setAddLoanClause] = useState("");
const handleSubmit = () => {
if (id) {
var loanProposalDetailsUpdateObject = {
...
addLoanClause: addLoanClause,
};
if (dataChanged(loanProposalDetailsUpdateObject)) {
updateUserLoanProposalRequest({
loanProposalDetails: loanProposalDetailsUpdateObject,
}).then(() => {
routeToNextPage(isUserLoanRequestInitiator, router, id);
});
}
else {
routeToNextPage(isUserLoanRequestInitiator, router, id);
}
} else {
props
.createUserLoanRequest({
requestType: transactionType,
status: "draft",
loanProposalDetails: {
...
addLoanClause: addLoanClause,
},
})
.then(data => {
routeToNextPage(isUserLoanRequestInitiator, router, data.id);
});
}
};
<DynamicWidthInput
id="addLoanClause"
value={addLoanClause}
type="textarea"
placeholder="1000 characters"
error={showErrors && getError("addLoanClause")}
onChange={e =>
handleDynamicStateChange(e, setAddLoanClause)
}
size={"xxl"}
rows="5"
/>
How can I achieve it if I want to send empty value in textarea and it will automatically know to send string "None" to the data?
You could define your value to be sent using a ternary referencing the length of the input:
const output = addLoanClause.length > 0 ? addLoanClause : 'None'
So if the length of the input is greater than zero, use the input. Else, use 'None'

How to start iterableDiffer only after OnInit?

My problem is that the iterableDiffer detect a change when I only what to inialize the data from the database. I dont want to get this first change detection. I only want to get a change detection when the user edits the array.
I tryed to put the database request in the constructor. Didnt change anything.
constructor(
private route: ActivatedRoute,
private StoreService: StoreService,
private iterableDiffers: IterableDiffers
) {
this.iterableDiffer =
this.iterableDiffers.find([]).create(null);
}
myArray: MyClass[] = [];
iterableDiffer: IterableDiffer<unknown>;
ngOnInit() {
const id = this.route.snapshot.params.id;
this.StoreService.getData(id)
.subscribe(data => this.myArray = data);
// on this subscribe, the Differ falsely gets triggered
}
ngAfterViewChecked() {
const changes = this.iterableDiffer.diff(this.myArray);
if (changes) {
changes.forEachAddedItem((record: IterableChangeRecord<MyClass>) => {
console.log('Added Changes detected');
this.StoreService.addToDatabase(record.item);
});
}
}
// gets called by User Click, here i want to have the Differ called
// This works fine
addElmt(elmt: MyClass) {
this.myArray.push(elmt);
}
The code snippet is a simplified version of the real code. I can not call the StoreService.addToDatabase() in the function addElmt(). This is just for better explanation.
Thank you for your help!
Always can has a variable 'myObserver' and do
myObserver:any=null
ngAfterViewChecked() {
const changes = this.iterableDiffer.diff(this.myArray);
if (changes) {
changes.forEachAddedItem((record: IterableChangeRecord<MyClass>) => {
console.log('Added Changes detected');
this.StoreService.addToDatabase(record.item);
});
}
if (!myObserver)
{
const id = this.route.snapshot.params.id;
myObserver=this.StoreService.getData(id)
.subscribe(data => this.myArray = data);
}
}

Categories