Fel uppstod under bearbetning av mallen.	
	
		
				
	
	
	
	
	
	
					
				
			
		
	
	
	The following has evaluated to null or missing:
==> cpProductId  [in template "44616#44647#3234203" at line 93, column 92]
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
	- Failed at: ${cpProductId}  [in template "44616#44647#3234203" at line 93, column 90]
----
	1<#assign 
				2commerceContext = renderRequest.getAttribute("COMMERCE_CONTEXT") 
				3channelId = commerceContext.getCommerceChannelId() 
				4/> 
				5<#if cpCatalogEntry?has_content> 
				6	<#assign 
				7	  cpDefinitionId = cpCatalogEntry.getCPDefinitionId() 
				8		cpProductId = cpCatalogEntry.getCProductId() 
				9	  productName = cpCatalogEntry.getName() 
				10	  productShortDescription = cpCatalogEntry.getShortDescription() 
				11	  productDescription = cpCatalogEntry.getDescription() 
				12	  productImages = cpContentHelper.getImages(cpDefinitionId, false, themeDisplay) 
				13		productImages = productImages?reverse 
				14		cpAttachmentFileEntries = cpContentHelper.getCPMedias(cpDefinitionId, themeDisplay) 
				15	/> 
				16 
				17		<nav class="breadcrumb-navigation" aria-label="breadcrumb"> 
				18			<ol class="breadcrumb-list"> 
				19				<li class="breadcrumb-item"><a id="level0CategoryCrumb" href='${languageUtil.get(locale, "what-we-offer-products-url")}' class="breadcrumb-link"></a></li> 
				20 
				21				<li  class="breadcrumb-item"><a id="level1CategoryCrumb" href="#" class="breadcrumb-link"></a></li> 
				22				<li class="breadcrumb-item" aria-current="page">${productName}</li> 
				23			</ol> 
				24		</nav>	 
				25	 
				26		<div class="category-detail"> 
				27			<h1 class="category-title">${productName}</h1> 
				28			<p class="category-description">${productShortDescription}</p>         
				29		</div>	 
				30	 
				31		<div class="product-catalog-detail-section"> 
				32				<div class="accordion tabs"> 
				33					<h2 class="toggle">Product Detail</h2> 
				34					<div class="content" tabindex="0"> 
				35						<div class="product-detail-content row align-items-lg-start align-items-sm-start align-items-start align-items-md-start flex-lg-row flex-sm-row flex-row flex-md-row"> 
				36							<div class="product-detail-slider-wrap col col-lg-5 col-sm-12 col-12 col-md-5"> 
				37								<span class="product-detail-image-paging-info visually-hidden" role="status"></span> 
				38								<div class="product-detail-slider"> 
				39								 <#if productImages?has_content> 
				40									<#list productImages as currentImage> 
				41										<li class="product-detail-slider-slide"> 
				42											 
				43											<img class="product-detail-slider-slide-img" src="${currentImage.getURL()}" width="428" height="428" alt="${currentImage.getTitle()}" /> 
				44										</li>	 
				45									</#list> 
				46								 </#if>	   
				47								</div> 
				48								<div class="product-detail-slider-nav"> 
				49								 <#if productImages?has_content> 
				50									<#list productImages as currentImage> 
				51										<li role="button" class="product-detail-slider-nav-btn"> 
				52											<img class="product-detail-slider-slide-img" src="${currentImage.getURL()}" width="65" height="65" alt="${currentImage.getTitle()}" /> 
				53										</li>	 
				54									</#list> 
				55								 </#if>	   
				56								 </div> 
				57							</div> 
				58							<div class="product-detail-content-desc col col-lg-7 col-sm-12 col-12 col-md-7"> 
				59								<p>${productDescription}</p> 
				60								<a class="btn btn-primary" href="/contact">Contact Sales</a> 
				61							</div>	    
				62						</div> 
				63					</div> 
				64 
				65						<#assign contentTypeMap = {}/> 
				66						<#assign youtubeVideoMap = []/> 
				67	 
				68					<#if cpAttachmentFileEntries?has_content> 
				69						<h2 class="toggle">Resources</h2> 
				70						<div  class="content" tabindex="0"> 
				71							<div id="resourcesDiv" > 
				72							</div> 
				73						</div> 
				74					</#if>	 
				75						<h2 class="toggle">Related Products</h2> 
				76						<div class="content" tabindex="0"> 
				77							<div id="relatedProductDiv"> 
				78	 
				79							</div> 
				80					 </div> 
				81 
				82							<h2 id="videoTitle" class="toggle">Videos</h2> 
				83							<div class="content" tabindex="0"> 
				84								<div id="videoDiv" > 
				85									<ul id="videoListSection" class="product-catalog-list-detail"></ul>		 
				86								</div> 
				87							</div> 		 
				88				</div>  
				89		</div>	 
				90</#if> 
				91	<script> 
				92 
				93	fetch('/o/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${cpProductId}/categories') 
				94  .then(response => response.json()) 
				95  .then(data => { 
				96		const productCategoryItems = data.items; 
				97    if (productCategoryItems && productCategoryItems.length > 0) { 
				98			productCategoryItems.forEach(function (productCategoryItem) { 
				99				const categoryId = productCategoryItem.id 
				100				fetch('/o/c/levelonecategories/?fields=categoryLiferayId,categoryName,categoryURL&filter=categoryLiferayId eq '+categoryId + '&p_auth='+ Liferay.authToken) 
				101				.then(response => response.json()) 
				102				.then(data => { 
				103					const level1Categories = data.items; 
				104					if (level1Categories && level1Categories.length > 0) { 
				105						level1Categories.forEach(function (level1Category) { 
				106							if(level1Category){ 
				107								const productCategoryTitle = level1Category.categoryName; 
				108								const productCategoryURL = 	level1Category.categoryURL; 
				109								const productCategoryFinalURL = 	"/categories/" + productCategoryURL; 
				110								document.getElementById('level1CategoryCrumb').setAttribute('href', productCategoryFinalURL); 
				111								document.getElementById('level1CategoryCrumb').innerHTML = productCategoryTitle; 
				112 
				113								const level0Hash = {  
				114									"Electricity Meters + Modules":"Measurement + Sensing", 
				115									"Gas Meters + Modules":"Measurement + Sensing", 
				116									"Sensing + Control":"Measurement + Sensing", 
				117									"Thermal Energy Meters + Modules":"Measurement + Sensing", 
				118									"Water Meters + Modules":"Measurement + Sensing", 
				119									"Grid Management":"Networks + Operations", 
				120									"Mobile Meter Reading":"Networks + Operations", 
				121									"Network Infrastructure + Management ":"Networks + Operations", 
				122									"Operations Management":"Networks + Operations", 
				123									"Smart Cities":"Networks + Operations", 
				124									"Distributed Energy Management":"Software + Services", 
				125									"Energy Forecasting":"Software + Services", 
				126									"Meter Data Management + Analytics":"Software + Services", 
				127									"Prepayment":"Software + Services", 
				128									"Services":"Software + Services", 
				129					        "CityEdge":"Portfolio Type", 
				130					        "Gas Edge":"Portfolio Type", 
				131					        "Grid Edge Intelligence":"Portfolio Type", 
				132					        "Smart Water Solutions":"Portfolio Type" 
				133								}; 
				134								 
				135								const level0CategoryName = level0Hash[productCategoryTitle]; 
				136								document.getElementById('level0CategoryCrumb').innerHTML = level0CategoryName; 
				137							} 
				138						}); 
				139						 
				140					} else { 
				141						console.log('No level1Categories found in the response.'); 
				142					} 
				143				}) 
				144				.catch(error => { 
				145					console.error('Error:', error); 
				146				}); 
				147				 
				148			}); 
				149 
				150    } else { 
				151      console.log('No productCategoryItems found in the response.'); 
				152    } 
				153  }) 
				154  .catch(error => { 
				155    console.error('Error:', error); 
				156  }); 
				157	fetch('/o/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${cpProductId}/attachments?accountId=0&pageSize=50') 
				158  .then(response => response.json()) 
				159  .then(data => { 
				160		const attachmentsMap = new Map(); 
				161		const youTubeMap = new Map(); 
				162    const productAttachmentItems = data.items.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0)); 
				163    if (productAttachmentItems && productAttachmentItems.length > 0) { 
				164			productAttachmentItems.forEach(function (productAttachmentItem) { 
				165      	const productAttachmentURL = productAttachmentItem.src.split("?")[0]; 
				166				const productAttachmentURLStripped = productAttachmentURL.replace("https://itronportal.lxc.liferay.com",""); 
				167      	const productAttachmentTitle = productAttachmentItem.title; 
				168				const productAttachmentCustomFields = productAttachmentItem.customFields 
				169				var productAttachmentContentType = "Article" 
				170				var displayAttachment = true; 
				171				productAttachmentCustomFields.forEach(function (productAttachmentCustomField) { 
				172					const productAttachmentCustomFieldName = productAttachmentCustomField.name 
				173					if(productAttachmentCustomFieldName == "ContentType"){ 
				174						productAttachmentContentType = productAttachmentCustomField.customValue.data; 
				175					} 
				176					if(productAttachmentCustomFieldName == "LanguageKey"){ 
				177						const productAttachmentLanguageKey = productAttachmentCustomField.customValue.data; 
				178						if((productAttachmentLanguageKey != null || productAttachmentLanguageKey != "") && productAttachmentLanguageKey != 'en_US'){ 
				179								displayAttachment = false; 
				180								if(productAttachmentLanguageKey === '${locale}'){ 
				181									displayAttachment = true; 
				182								} 
				183					} 
				184				} 
				185				}); 
				186 
				187						if(productAttachmentContentType == "YouTube Video"){ 
				188							const productAttachmentFileEntryId = productAttachmentItem.fileEntryId; 
				189							fetch('/o/headless-delivery/v1.0/documents/' + productAttachmentFileEntryId) 
				190							.then(response => response.json()) 
				191							.then(data => { 
				192								const contentCustomFields = data.customFields; 
				193								if (contentCustomFields) { 
				194									let youTubeURL = ""; 
				195									let youTubeCode = ""; 
				196									let videoTitle = ""; 
				197									contentCustomFields.forEach(function (contentCustomField) { 
				198										const contentName = contentCustomField.name 
				199										 
				200										if(contentName == "VideoUrl"){ 
				201											youTubeURL = contentCustomField.customValue.data; 
				202											const splitURL = youTubeURL.split("/"); 
				203											const splitSize = splitURL.length; 
				204											youTubeCode = splitURL[splitSize-1]; 
				205										} 
				206										if(contentName == "VideoTitle"){ 
				207											videoTitle = contentCustomField.customValue.data; 
				208										} 
				209									}); 
				210									let videoListItem = '<li class="product-catalog-list-item-detail"><button class="btn btn-link youtube-modal-link global-youtube-modal-trigger" data-bs-toggle="modal" data-bs-target="#global-youtube-modal" data-modal-url="'+youTubeCode+'" data-modal-title="Itron Optimizer Portfolio Overview"><div class="card-inner"><div class="card-image-div"><img width="279" height="157" loading="lazy" alt="" src="//img.youtube.com/vi/'+youTubeCode+'/maxresdefault.jpg" /><span class="modal-video-btn"><span class="visually-hidden">Play Video</span></span></div><div class="card-body"><h2 class="card-title">'+videoTitle+'</h2></div></div></button></li>'; 
				211								  let existingVideoList = document.getElementById('videoListSection').innerHTML; 
				212									 
				213									document.getElementById('videoListSection').innerHTML = existingVideoList + videoListItem; 
				214									showHideVideoSection(); 
				215								} else { 
				216									console.log('No contentCustomFields found in the response.'); 
				217								} 
				218							}) 
				219							.catch(error => { 
				220								console.error('Error:', error); 
				221							}); 
				222						} else { 
				223							if(displayAttachment){ 
				224								const listItem = "<li><a target='_blank' href="+ productAttachmentURLStripped+" class='list-link'>"+productAttachmentTitle+"</a></li>"; 
				225								let mapValue = attachmentsMap.get(productAttachmentContentType); 
				226								let newMapValue = ""; 
				227								if(mapValue){ 
				228									let newMapValue = mapValue + listItem; 
				229									attachmentsMap.set(productAttachmentContentType, newMapValue); 
				230								} else { 
				231									attachmentsMap.set(productAttachmentContentType, listItem); 
				232								} 
				233							} 
				234					  } 
				235			}); 
				236 
				237			let totalList = ''; 
				238			let sortedMap = new Map([...attachmentsMap.entries()].sort()); 
				239			for (const [key, value] of sortedMap.entries()) { 
				240				let ulist = '<h2 class="list-title">' + key + '</h2><ul class="list">' + value + '</ul>' 
				241				totalList = totalList + ulist; 
				242			} 
				243			 
				244			document.getElementById('resourcesDiv').innerHTML = totalList; 
				245    } else { 
				246      console.log('No attachments for selected product.'); 
				247    } 
				248  }) 
				249  .catch(error => { 
				250    console.error('Error:', error); 
				251  }); 
				252		 
				253		 
				254fetch(`/o/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${cpProductId}/related-products`) 
				255  .then(response => response.json()) 
				256  .then(data => { 
				257    let relatedList = '<ul class="product-catalog-list-detail">'; 
				258    const relatedProductItems = data.items; 
				259    const totalCount = data.totalCount; 
				260    let fetchedProducts = []; // Array to store products with names 
				261    let fetchCount = 0; // Counter for completed fetch requests 
				262 
				263    if (relatedProductItems && relatedProductItems.length > 0) { 
				264      relatedProductItems.forEach((relatedProductItem) => { 
				265        const relatedProductId = relatedProductItem.productId; 
				266 
				267        fetch('/o/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/' + relatedProductId) 
				268          .then((response) => response.json()) 
				269          .then((productData) => { 
				270            if (productData) { 
				271              const relatedProductName = productData.name; 
				272              const relatedProductImageURL = productData.urlImage; 
				273              const relatedProductFriendlyURL = productData.urls['en_US'] || ''; 
				274 
				275              // Push product details to the fetchedProducts array 
				276              fetchedProducts.push({ 
				277                name: relatedProductName, 
				278                imageURL: relatedProductImageURL, 
				279                friendlyURL: relatedProductFriendlyURL, 
				280              }); 
				281            } 
				282          }) 
				283          .catch((error) => { 
				284            console.error("Error fetching product details:", error); 
				285          }) 
				286          .finally(() => { 
				287            fetchCount++; 
				288            if (fetchCount === relatedProductItems.length) { 
				289              // Sort products by name after all fetches are completed 
				290              fetchedProducts.sort((a, b) => a.name.localeCompare(b.name)); 
				291 
				292              // Generate the list items 
				293              fetchedProducts.forEach((product) => { 
				294                if (product.name) { 
				295                  relatedList += '<li class="product-catalog-list-item-detail"><a class="product-catalog-list-item-link" href="/products/' + product.friendlyURL + '"><div class="card-inner"><div class="card-image-div"><img class="product-catalog-list-item-img" width="227" height="227" loading="lazy" src="'+product.imageURL+'"alt="'+product.name+'" /></div><div class="card-body"><h2 class="card-title card-title__xl">' +product.name + '</h2></div></div></a></li>'; 
				296                } 
				297              }); 
				298 
				299              // Close the list and update the DOM 
				300              relatedList += '</ul>'; 
				301              document.getElementById('relatedProductDiv').innerHTML = relatedList; 
				302              showHideRelatedProductsSection(); 
				303            } 
				304          }); 
				305      }); 
				306    } else { 
				307      console.log("No relatedProductItems found in the response."); 
				308    } 
				309  }) 
				310  .catch((error) => { 
				311    console.error("Error fetching related products:", error); 
				312  }); 
				313 
				314 
				315	</script> 
		
	Region Selector
	Select a region and country for the best experience.