I am working on React/Redux e-commerce project, I want to display company logo badge in PLP(Product Listing Page) whether the product is fullified by company or not, The code is working fine in PLP (Product listing page) but when I navigate back to home page than it gives an error, can any one help me and guide me where I am doing wrong, My all code, API response and Error is attached below.
const price = (
<div
className={styles.productPrice}
itemProp="offers"
itemScope
itemType="http://schema.org/Offer"
>
<span className={styles.sellingPrice} itemProp="price">
{product.offer.salePrice
? I18n.formatCurrency(product.offer.salePrice, { valueClass: styles.priceValue })
: I18n.formatCurrency(product.offer.price, { valueClass: styles.priceValue })}
</span>
{product.offer.salePrice && <span className={styles.preReduction}>
<span>{'productBox.pre-reduction', {}, 'was'}</span> {I18n.formatCurrency(product.offer.price)}
</span>}
</div>
);
const productName = (lines) =>
<Shiitake lines={lines} throttleRate={200} className={styles.productName}>
{product.name}
</Shiitake>;
const brandName = product.brand && product.brand.name ?
<Shiitake lines={1} throttleRate={200} className={styles.brandName}>
{product.brand.name}
</Shiitake> : '';
const soldBy = <div className={styles.sellerCtr}>
{ product && catalog && catalog.hits[0].is_fbn &&
<div className={styles.sellerFulfillmentCtr}>
<ShippingBadges showFulfillment />
</div>
}
</div>
Above constants are displayed via this code
<div className={styles.productDetailsContainer}>
{brandName}
{productName(LINES_TO_SHOW)}
{showAdditionalInfo &&
<div>
{hasReviews &&
<div className={styles.ratingBadgesCtr}>
<StarsIconRating
size={11}
score={parseInt(product.review.score) || 0}
count={parseInt(product.review.count) || 0}
showCount
/>
</div>}
</div>}
{product.offer && price}
{soldBy}
</div>}
API Response
Error Screen
The error indicates that, for some reason, your catalog object doesn't have a .hits array. The provided code doesn't, as far as I can see, explain why that would be the case.
You should be able to stop the error (or generate a new error further down) by changing product && catalog && catalog.hits[0].is_fbn to product && catalog && catalog.hits && catalog.hits[0].is_fbn. (Checking that the hits array exists before we try to access index 0) . But that would also mean that the <ShippingBadges > components won't be rendered. I'm not sure what the correct behavior is here.
If this data (catalog.hits) is saved in (and read from) Redux state, then it's surprising that it would disappear just because you navigate somewhere (unless you have some action clearing the state). You could debug Redux state related issues with Redux devtools (for Firefox or Chrome), and/or by putting breakpoints and/or console.logs in the mapStateToProps function you may use to connect Redux to the component that fails.
If, on the other hand, the issue is that this code (the one that generates the error) shouldn't even run when you navigate to "home page", then the issue could be something else entirely.
Try to see in your life cycle, because in that first render your catalog.hits can be undefined because it's not loaded completely. Try
before render if(catalog.hits !== undefined).
Related
I have a survey, that people can take. However, if you are one of the first 1000 to fill out the survey you are given a reimbursement. What I am struggling with is how to only show the reimbursement when the users are less than 1000.
The questions in the survey are coming from a CMS backend, while the reimbursement is built on the front end.
<div class="questionContainer" v-if="intentGiven && consentGiven && !surveyComplete">
<div v-if="$store.state.scoring.gameCompleted && this.index == this.QuestionnaireSize && this.ParticipantCount <= 1000">
<ResearchQuestionReimbursement v-on:answered="advanceQuestion"/>
</div>
<div v-else>
<ResearchQuestionMultipleChoice :question="questions[index]"
v-if="questions[index].Type == 'MultipleChoice'"
v-on:answered="advanceQuestion" />
<ResearchQuestionFreeAnswer :question="questions[index]"
v-if="questions[index].Type == 'FreeAnswer'"
v-on:answered="advanceQuestion" />
</div>
</div>
questions[index] is an array of the questions received from the backend CMS. The reimbursement is only supposed to be at the end which is why I have the div wrapped around <ResearchQuestionReimbursement>.
My control logic for the index is:
if (this.index < this.questions.length -1 || (this.$store.state.scoring.gameCompleted && this.index < this.questions.length)) {
this.index++;
console.log(`post: ${this.index}`)
} else {
.....
//required code to save the form
}
Currently, if I have over 1000 users the logic continues and gives me an index undefined error. This makes sense since the index would be 13 and the array would only be 12. I have tried a v-if on <ResearchQuestionReimbursement> to check if there are 1000 users. This resulted in a blank page rendering since it rendered the div but not the content.
What it should do is if there are more than 1000 users it will skip to the else statement.
I have a div inside which I'm rendering dynamic card components(SearchRes):
<div className="search-res">
{games.filter(game => (game.background_image !== null)&&(game.ratings_count>38)).map(game=>
<SearchRes
key={game.name}
title={game.name}
rating={game.metacritic===null?<MdGamepad/>:game.metacritic}
genre={game.genres.map(genre=>genre.name+" ")}
imglnk={(game.background_image===null)?"":game.background_image}
gameid={game.id}
slug={game.slug}
plat={game.parent_platforms}
/>
)}
</div>
When the cards don't render (when they don't pass the filter conditions), I get a blank space. I want to display something like no results found when such thing happens, how can I implement this?
Currently I'm using CSS to implement it:
.search-res:empty::before {
content: "No data";}
but clearly this is not viable as the message is visible when the search is not even initiated:
If I understand your question, you want to conditionally render a "No Data" fallback only after you've fetched data and there is nothing to render.
Add a loading state and conditionally render a loading indicator.
Filter the games and in the render check that there is an array length (i.e. something to map), and conditionally render the array or the fallback text if there is a current search term.
Code
const [searchLoaded, setSearchLoaded] = React.useState(false);
// in search handler
// toggle searchLoaded false when initiating a search
// toggle searchLoaded true when search completes
const results = games.filter(
game => game.background_image !== null && game.ratings_count > 38
);
<div className="search-res">
{results.length
? results.map(game => (
<SearchRes
key={game.name}
title={game.name}
rating={game.metacritic === null ? <MdGamepad/> : game.metacritic}
genre={game.genres.map(({ name }) => name).join(" ")}
imglnk={game.background_image === null ? "" : game.background_image}
gameid={game.id}
slug={game.slug}
plat={game.parent_platforms}
/>
))
: searchLoaded && "No Data"
}
</div>
I would suggest to filter the array first, based on the conditions and then check if the array has length i.e. arr.length !== 0 and if that is the case, then map the results of the array to the card component. And if not then show the message that no data found. You would have to use if/else or ternary operator.
Thanks😊
if you are using a filter only for conditioning then there is no need for a filter instead you can use conditional rendering like this:
<div className="search-res">
{games.map(game=>
{(game.background_image !== null && game.ratings_count>38) ?
<SearchRes
key={game.name}
title={game.name}
rating={game.metacritic===null?<MdGamepad/>:game.metacritic}
genre={game.genres.map(genre=>genre.name+" ")}
imglnk={(game.background_image===null)?"":game.background_image}
gameid={game.id}
slug={game.slug}
plat={game.parent_platforms}
/> : "NO RESULTS FOUND"}
)}
</div>
I have a blog with some posts. When you click on the preview you will redirect on the page post.
On the page of the post, I use a getter to load the correct post (I use the find function to return object.name which corresponds to the correct object in the array of objects).
const state = {
ricettario: [], // data that contains all recipes (array of objects)
}
const actions = {
// Bind State and Firestore collection
init: firestoreAction(({ bindFirestoreRef }) => {
bindFirestoreRef('ricettario', db.collection('____').orderBy('data'))
})
const getters = {
caricaRicetta(state) {
console.log('Vuex Getter FIRED => ', state.ricettario)
return nameParamByComponent => state.ricettario.find(ricetta => {
return ricetta.name === nameParamByComponent
})
}
}
In the component, I call the getter in the computed property
computed: {
...mapGetters('ricettaStore', ['caricaRicetta']),
ricetta() {
return this.caricaRicetta(this.slug) // this.slug is the prop of the URL (by Router)
}
}
Anything goes in the right way but when I reload the page in the POST PAGE, the getter will fire 2 times:
1. return an error because the state is null
2. return the correct object
// screen below
So everything works fine from the front but not at all in the console and in the App.
I think the correct way is to call the getters in the created hook. What I've to change? It is a problem with the computed prop, getters or state?
POST PAGE:
<template>
<div v-if="ricetta.validate === true" id="sezione-ricetta">
<div class="container">
<div class="row">
<div class="col s12 m10 offset-m1 l8 offset-l2">
<img
class="img-fluid"
:src="ricetta.img"
:alt="'Ricetta ' + ricetta.titolo"
:title="ricetta.titolo"
/>
</div>
</div>
</div>
</div>
<div v-else>
...
</div>
</template>
You are trying to validate undifined property. So you need to check ricetta first.
Try like this:
<div v-if="ricetta && ricetta.validate === true" id="sezione-ricetta">
Database synchronization is asynchronous, ricettario is initially an empty array. Computed value is recomputed once synchronization is finished and ricettario array is filled, the component is updated.
Even if ricettario weren't empty, find may return undefined if it finds nothing. This needs to be handled where ricetta is used:
<div v-if="ricetta && ricetta.validate" id="sezione-ricetta">
The error log is quite explicit, there is a xxx.validate somewhere in your Ricetta component template, but that xxx is undefined.
Because of this, your app crashes and stops working. I doubt it has anything to do with Vuex
Issue overview: Currently coding a Lightning Component to update records on a custom object. However, every time I trigger the update (via a ui:button), the page freezes and I don't see any errors in the debugger or console. Cannot for the life of me figure out why it won't work.
Context: The component has a number of dropdowns that are populated with records (with the label being the record name). Selecting a new value in the dropdown and hitting "Update" calls the below apex to change a custom field (Status__c = 'Ready') on the new selected item, and then updates the records that occur before it (Status__c = 'Complete). I do all of my security and update checks in another function during init, so you won't see that here (I can post the full code if needed). Just trying to get the update to work.
I would be eternally grateful if someone could show me the error of my ways :]. Always been a huge fan of stackoverflow and looking forward to contributing now that I finally signed up. Thanks for your time everyone!
Apex:
#AuraEnabled
public static void updateMilestones(String deployId,Boolean prodChanged,String newProdMile) {
if( prodChanged == true && newProdMile != null ) {
try {
decimal newProdStepNum;
List <Milestone__c> newReadyProdMile = New List<Milestone__c>();
for(Milestone__c mil1:[SELECT id, Status__c, Step_Order__c FROM Milestone__c
WHERE Deployment__c = :deployID
AND id = :newProdMile LIMIT 1]){
mil1.Status__c = 'Ready';
newProdStepNum = mil1.Step_Order__c;
newReadyProdMile.add(mil1);
}
List <Milestone__c> prodMilesComplete = New List<Milestone__c>();
for(Milestone__c mil2:[SELECT id, Type__C, Status__c FROM Milestone__c
WHERE Deployment__c = :deployID
AND Step_Order__c < :newProdStepNum
AND Type__c = 'Production'
AND Status__c != 'Complete'
AND Status__c != 'Revised']){
mil2.Status__c = 'Complete';
prodMilesComplete.add(mil2);
}
update newReadyProdMile;
update prodMilesComplete;
}
catch (DmlException e) {
throw new AuraHandledException('Sorry, the update did not work this time. Refresh and try again please!');
}
}
}
Javascript:
updateMilestones : function(component, event, helper) {
// update milestones server-side
var action = component.get("c.updateMilestones");
action.setParams({
deployId : component.get("v.recordId"),
newProdMile : component.find("prod-mile-select").get("v.value"),
prodChanged : component.get("v.prodChanged")
});
// Add callback behavior for when response is received
action.setCallback(this, function(response) {
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
// re-run the init function to refresh the data in the component
helper.milesInit(component);
// refresh record detail
$A.get("e.force:refreshView").fire();
// set Update Changed Milestones button back to disabled
component.find("updateButton").set("v.disabled","true");
// show success notification
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"message": "Milestones have been updated successfully."
});
toastEvent.fire();
}
});
// Send action off to be executed
$A.enqueueAction(action);
}
Component:
<aura:component controller="auraMilestonesController_v2"
implements="force:appHostable,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction">
<ltng:require scripts="{!$Resource.lodash}" afterScriptsLoaded="{!c.doInit}"/>
<aura:attribute name="recordId" type="String" />
<aura:attribute name="prodMiles" type="Milestone__c[]"/>
<aura:attribute name="prodChanged" type="Boolean" default="false"/>
<!-- FORM -->
<div class="slds-col slds-col--padded slds-p-top--large" id="theform">
<form class="slds-form--stacked">
<!-- UPDATE BUTTON -->
<div class="slds-form-element">
<ui:button aura:id="updateButton" label="Update Changed Milestones" press="{!c.updateMilestones}"
class="slds-button slds-button--brand slds-align--absolute-center" disabled="true"/>
</div>
<hr style="color: #005fb2;background-color: #005fb2;"/>
<!-- PRODUCTION -->
<div aura:id="prod-section">
<div class="slds-form-element">
<label class="slds-form-element__label" for="milestone">Production Milestone</label>
<div class="slds-form-element__control">
<div class="slds-select_container">
<ui:inputSelect aura:id="prod-mile-select" class="slds-select" change="{!c.prodChange}">
<option value="" >--Select One--</option>
<aura:iteration items="{!v.prodMiles}" var="m">
<aura:if isTrue="{!m.Status__c == 'Ready'}">
<option value="{!m.id}" selected="true">{!m.Name} ({!m.User_Name__c})</option>
</aura:if>
<aura:if isTrue="{!m.Status__c == 'Not Ready'}">
<option value="{!m.id}">{!m.Name} ({!m.User_Name__c})</option>
</aura:if>
</aura:iteration>
<option value="completeProdMile" id="completeProdMile">All Production Milestones Complete</option>
</ui:inputSelect>
</div>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="description">Description</label>
<div class="slds-textarea">
<aura:iteration items="{!v.prodMiles}" var="m">
<aura:if isTrue="{!m.Status__c == 'Ready'}">{!m.Description__c}</aura:if>
<!-- <aura:set attribute="else">All production milestones have been completed.</aura:set> -->
</aura:iteration>
</div>
<hr style="color: #005fb2;background-color: #005fb2;"/>
</div>
</div>
<!-- END PRODUCTION -->
</form>
</div>
<!-- / FORM -->
</aura:component>
I believe the issue is that you have fallen into the all too common trap of naming both a client side and a server side controller method the same (updateMilestones in this case). Try changing the name of either to make them unique and I expect that will get you running.
Yes, there is a bug on this and many of us have been making a very loud noise about getting it fixed!
Also we have a very active Salesforce specific Stack Exchange forum over here https://salesforce.stackexchange.com/ that will get more attention - especially if you tag your posts with lightning-components (e.g. I have my account configured to send me an email alert on every post tagged with lightning-components, locker-service, etc).
That might be javascript causing the error.As it's difficult to solve without knowing the error, I would suggest you debug the error.
Turn on debug mode.
a. From Setup, click Develop > Lightning Components.
b. Select the Enable Debug Mode checkbox.
c. Click Save.
In Chrome Developer Tools, check the "Pause on Caught Exceptions" checkbox in the Sources tab. This can often help finding the source of an issue. Other browsers may have similar options.
Add a debugger statement if you want to step through some code.
debugger;
This is useful when you have a rough idea where the problem might be happening.
debugger
https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/debug_intro.htm
I'm new to programming and I'm learning Facebook's react.js. I found a website that has a tutorial which builds a "shopping cart" using react. I tried to modify it to add more items using a for loop but it keeps giving me "unexpected token }" errors followed by this error:
"Invariant Violation: FluxProduct.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object."
I realize there is a similar question that was answered but it didn't help me.
There is quite a bit of code in this project but the particular spot I'm having trouble with looks like this:
render: function() {
var rows = [];
var ats = (this.props.selected.sku in this.props.cartitems) ?
this.props.selected.inventory - this.props.cartitems[this.props.selected.sku].quantity :
this.props.selected.inventory;
for (var i=0; i<2; i++){<div className="flux-products">
<img src={'img/' + this.props.product.image}/>
<div className="flux-products-detail">
<h1 className="name">{this.props.product.name}</h1>
<p className="description">{this.props.product.description}</p>
<p className="price">Price: ${this.props.selected.price}</p>
<select onChange={this.selectVariant}>
{this.props.product.variants.map(function(variant, index){
return (
<option key={index} value={index}>{variant.type}</option>
)
})}
</select>
<button type="button" onClick={this.addToCart} disabled={ats > 0 ? '' : 'disabled'}>
{ats > 0 ? 'Add To Cart' : 'Sold Out'}
</button>
</div>
</div>}
return ("flux-products-detail"
);
},
});
If you want/need the original code and the rest of the project I'd be more than happy to provide it.
It looks like you are trying to compose three components in the for loop. However the result of the for loop is not stored to a variable, and not rendered properly in the return function.
// Use a variable to store the result of the loop
return (
<div>
{myComponent}
</div>
)
In the case of using a loop to generate content for a React element, I usually formulate it something like this:
render: function() {
var items = [];
for(var i = 0; i < this.state.listOfItems.length; i++) {
items.push( <ItemComponent value={this.state.listOfItems[i].value} />
}
return ( <div> {items} </div>)
};
So what you need to do is return from render some JSX for react to Render. as #FelixKling said, JSX isn't magic, you're essentially just returning a bunch of React.createElement functions.