Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_9761541d70404c1ba07697af2c1277d3.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 6 @{ 7 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 8 9 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 10 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 11 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 12 13 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 14 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 15 Pageview.Meta.AddTag("twitter:description", metaDescription); 16 } 17 18 @{ 19 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 20 { 21 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 22 } 23 else 24 { 25 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 26 } 27 28 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 29 if (isLazyLoadingForProductInfoEnabled) 30 { 31 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 32 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 33 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 34 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 35 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 36 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 37 ?UserId={Converter.ToString(Pageview.User?.ID)} 38 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 39 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 40 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 41 &PriceSettings.FilledProperties={priceFilledProperties} 42 &getproductinfo=true"; 43 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 44 45 <script type="module"> 46 swift.LiveProductInfo.init(); 47 </script> 48 } 49 } 50 51 <script> 52 gtag("event", "view_item", { 53 currency: "@Model.Price.CurrencyCode", 54 value: @PriceViewModelExtensions.ToStringInvariant(Model.Price), 55 items: [ 56 { 57 item_id: "@Model.Number", 58 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 59 currency: "@Model.Price.CurrencyCode", 60 price: @PriceViewModelExtensions.ToStringInvariant(Model.Price) 61 } 62 ] 63 }); 64 </script> 65 66 <script> 67 window.addEventListener('load', function (event) { 68 swift.Video.init(); 69 }); 70 </script> 71
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.cshtml" System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_af1417c3caea41ac8d66b1c976684d0e.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 30 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 31 32 ratioSettings.Ratio = ratio; 33 ratioSettings.CssClass = cssClass; 34 ratioSettings.CssVariable = cssVariable; 35 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 36 37 return ratioSettings; 38 } 39 40 public string GetColumnClass(int total, int assetNumber) { 41 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 42 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 43 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 44 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 45 46 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 53 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 54 55 return colClass; 56 } 57 58 public string GetArrowsColor() 59 { 60 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 61 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 62 return arrowsColor; 63 } 64 65 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 66 { 67 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 68 string type = GetVideoType(asset.Value); 69 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 70 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 71 72 var videoParams = new Dictionary<string, object>(); 73 videoParams.Add("AssetName", asset.Name); 74 videoParams.Add("AssetVideoType", type); 75 videoParams.Add("AssetDisplayName", asset.DisplayName); 76 videoParams.Add("OpenVideoInModal", openInModal); 77 videoParams.Add("VideoAutoPlay", autoPlay); 78 videoParams.Add("Size", size); 79 videoParams.Add("Id", Model.ID); 80 return videoParams; 81 } 82 83 public string GetVideoType(string assetValue) 84 { 85 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 86 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 87 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 88 return type; 89 } 90 91 public string GetYoutubeScreenDump(string assetValue, string quality) 92 { 93 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 94 Match match = regex.Match(assetValue); 95 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 96 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 97 return youtubeThumbnail; 98 } 99 } 100 101 @{ 102 @* Get the product data *@ 103 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 104 { 105 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 106 } 107 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 108 { 109 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 110 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 111 112 if (productList?.Products is object) 113 { 114 product = productList.Products[0]; 115 } 116 } 117 } 118 119 @if (product is object) 120 { 121 @* Supported formats *@ 122 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 123 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 124 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 125 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 126 127 @* Collect the assets *@ 128 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 129 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 130 131 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 132 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 133 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 134 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 135 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 136 assetsList = assetsList.Union(assetsImages); 137 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 138 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 139 140 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 141 142 int totalAssets = 0; 143 foreach (MediaViewModel asset in assetsList) { 144 var assetValue = asset.Value; 145 foreach (string format in allSupportedFormats) { 146 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 147 totalAssets++; 148 } 149 } 150 } 151 152 if (totalAssets == 0) 153 { 154 if (defaultImageFallback) { 155 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 156 totalAssets = 1; 157 } else { 158 assetsList = new List<MediaViewModel>(){ }; 159 totalAssets = 0; 160 } 161 } 162 163 @* Layout settings *@ 164 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 165 spacing = spacing == "none" ? "gap-0" : spacing; 166 spacing = spacing == "small" ? "gap-3" : spacing; 167 spacing = spacing == "large" ? "gap-4" : spacing; 168 169 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 170 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 171 172 var badgeParms = new Dictionary<string, object>(); 173 badgeParms.Add("size", "h5"); 174 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 175 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 176 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 177 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 178 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 179 180 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 181 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 182 DateTime createdDate = product.Created.Value; 183 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 184 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 185 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 186 187 188 @* Get assets from selected categories or get all assets *@ 189 if (totalAssets != 0 && assetsList.Count() != 0) { 190 int desktopAssetNumber = 0; 191 int mobileAssetNumber = 0; 192 int mobileAssetThumbnailNumber = 0; 193 int modalAssetNumber = 0; 194 195 @* Desktop: Show the gallery on large screens *@ 196 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 197 <div class="grid @spacing"> 198 @foreach (MediaViewModel asset in assetsList) { 199 var assetName = asset.Value; 200 foreach (string format in allSupportedFormats) { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 202 string size = "desktop"; 203 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 204 string assetValue = asset.Value; 205 206 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 207 <div class="h-100 @(imageTheme)"> 208 @foreach (string imageFormat in supportedImageFormats) 209 { //Images 210 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string productName = product.Name; 213 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 214 string imageLinkPath = Uri.EscapeDataString(imagePath); 215 216 RatioSettings ratioSettings = GetRatioSettings(size); 217 218 var parms = new Dictionary<string, object>(); 219 parms.Add("alt", productName); 220 parms.Add("itemprop", "image"); 221 if (totalAssets > 1) 222 { 223 parms.Add("columns", 2); 224 } 225 else 226 { 227 parms.Add("columns", 1); 228 } 229 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 230 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 231 232 if (!string.IsNullOrEmpty(asset.DisplayName)) 233 { 234 parms.Add("title", asset.DisplayName); 235 } 236 237 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 238 { 239 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 240 } 241 else 242 { 243 parms.Add("cssClass", "mw-100 mh-100"); 244 } 245 246 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 247 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 248 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 249 </div> 250 </a> 251 } 252 } 253 @foreach (string videoFormat in supportedVideoFormats) 254 { //Videos 255 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 256 { 257 if (Model.Item.GetString("OpenVideoInModal") == "true") 258 { 259 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 260 261 string type = GetVideoType(asset.Value); 262 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 263 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 264 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 265 266 string productName = product.Name; 267 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 268 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 269 270 RatioSettings ratioSettings = GetRatioSettings(size); 271 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 272 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 273 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 274 @if (type != "selfhosted") 275 { 276 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 277 } 278 else 279 { 280 string videoType = Path.GetExtension(asset.Value).ToLower(); 281 282 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 283 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 284 </video> 285 } 286 </div> 287 </div> 288 289 } 290 else 291 { 292 var videoParams = GetVideoParams(asset, size); 293 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 294 } 295 } 296 } 297 @foreach (string documentFormat in supportedDocumentFormats) 298 { //Documents 299 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 300 { 301 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 302 303 string productName = product.Name; 304 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 305 string imageLinkPath = Uri.EscapeDataString(imagePath); 306 307 RatioSettings ratioSettings = GetRatioSettings(size); 308 309 var parms = new Dictionary<string, object>(); 310 parms.Add("alt", productName); 311 parms.Add("itemprop", "image"); 312 parms.Add("fullwidth", true); 313 parms.Add("columns", Model.GridRowColumnCount); 314 if (!string.IsNullOrEmpty(asset.DisplayName)) 315 { 316 parms.Add("title", asset.DisplayName); 317 } 318 319 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 320 { 321 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 322 } 323 else 324 { 325 parms.Add("cssClass", "mw-100 mh-100"); 326 } 327 328 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 329 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 330 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 331 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 332 { 333 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 334 } 335 else 336 { 337 338 } 339 </div> 340 </a> 341 } 342 } 343 </div> 344 </div> 345 desktopAssetNumber++; 346 } 347 } 348 } 349 </div> 350 351 @if (showBadges) { 352 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 353 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 354 </div> 355 } 356 </div> 357 358 @* Mobile: Show the thumbs on small screens *@ 359 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 360 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 361 <div class="carousel-inner h-100"> 362 @foreach (MediaViewModel asset in assetsList) { 363 var assetValue = asset.Value; 364 foreach (string format in allSupportedFormats) { 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 367 string size = "mobile"; 368 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 369 370 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 371 <div class="h-100 @(imageTheme)"> 372 @foreach (string imageFormat in supportedImageFormats) 373 { //Images 374 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 375 { 376 string productName = product.Name; 377 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 378 string imageLinkPath = Uri.EscapeDataString(imagePath); 379 380 RatioSettings ratioSettings = GetRatioSettings(size); 381 382 var parms = new Dictionary<string, object>(); 383 parms.Add("alt", productName); 384 parms.Add("itemprop", "image"); 385 if (totalAssets > 1) 386 { 387 parms.Add("columns", 2); 388 } 389 else 390 { 391 parms.Add("columns", 1); 392 } 393 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 394 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 395 396 if (!string.IsNullOrEmpty(asset.DisplayName)) 397 { 398 parms.Add("title", asset.DisplayName); 399 } 400 401 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 402 { 403 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 404 } 405 else 406 { 407 parms.Add("cssClass", "mw-100 mh-100"); 408 } 409 410 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 411 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 412 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 413 </div> 414 </a> 415 } 416 } 417 @foreach (string videoFormat in supportedVideoFormats) 418 { //Videos 419 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 420 { 421 if (Model.Item.GetString("OpenVideoInModal") == "true") 422 { 423 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 424 425 string type = GetVideoType(asset.Value); 426 427 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 428 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 429 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 430 431 string productName = product.Name; 432 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 433 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 434 435 RatioSettings ratioSettings = GetRatioSettings(size); 436 437 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 438 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 439 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 440 @if (type != "selfhosted") 441 { 442 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" > 443 } 444 else 445 { 446 string videoType = Path.GetExtension(asset.Value).ToLower(); 447 448 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 449 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 450 </video> 451 } 452 </div> 453 </div> 454 } 455 else 456 { 457 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 458 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 459 } 460 } 461 } 462 </div> 463 </div> 464 mobileAssetNumber++; 465 } 466 } 467 } 468 </div> 469 </div> 470 471 @if (totalAssets > 1) { 472 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 473 @foreach (MediaViewModel asset in assetsList) { 474 var assetValue = asset.Value; 475 foreach (string format in allSupportedFormats) { 476 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 477 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 478 string type = GetVideoType(asset.Value); 479 480 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 481 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 482 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 483 484 string productName = product.Name; 485 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 486 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 487 488 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 489 @foreach (string imageFormat in supportedImageFormats) 490 { //Images 491 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 492 { 493 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 494 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle > 495 } 496 } 497 @foreach (string videoFormat in supportedVideoFormats) 498 { //Videos 499 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 500 { 501 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 502 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 503 </div> 504 if (type != "selfhosted") 505 { 506 507 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 508 509 } 510 else 511 { 512 string videoType = Path.GetExtension(asset.Value).ToLower(); 513 514 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 515 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 516 </video> 517 } 518 } 519 } 520 @foreach (string documentFormat in supportedDocumentFormats) 521 { //Documents 522 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 523 { 524 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 525 526 <a href="@Uri.EscapeDataString(assetValue)" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 527 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 528 { 529 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 530 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 531 </div> 532 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 533 } 534 else 535 { 536 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 537 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 538 </div> 539 } 540 </a> 541 } 542 } 543 </div> 544 545 mobileAssetThumbnailNumber++; 546 } 547 } 548 } 549 </div> 550 } 551 552 @if (showBadges) { 553 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 554 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 555 </div> 556 } 557 </div> 558 559 @* Modal with slides *@ 560 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 561 <div class="modal-dialog modal-dialog-centered modal-xl"> 562 <div class="modal-content"> 563 <div class="modal-header visually-hidden"> 564 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 565 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 566 </div> 567 <div class="modal-body p-2 p-lg-3 h-100"> 568 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 569 <div class="carousel-inner h-100 @theme"> 570 @foreach (MediaViewModel asset in assetsList) { 571 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 572 foreach (string format in allSupportedFormats) { 573 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 574 string imagePath = assetValue; 575 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 576 577 var parms = new Dictionary<string, object>(); 578 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 579 parms.Add("columns", Model.GridRowColumnCount); 580 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 581 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 582 583 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 584 @foreach (string imageFormat in supportedImageFormats) { 585 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 586 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 587 } 588 } 589 590 @foreach (string videoFormat in supportedVideoFormats) { 591 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 592 593 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 594 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 595 596 } 597 } 598 </div> 599 600 modalAssetNumber++; 601 } 602 } 603 } 604 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 605 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 606 <span class="visually-hidden">@Translate("Previous")</span> 607 </button> 608 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 609 <span class="carousel-control-next-icon" aria-hidden="true"></span> 610 <span class="visually-hidden">@Translate("Next")</span> 611 </button> 612 </div> 613 </div> 614 </div> 615 </div> 616 </div> 617 </div> 618 } else if (Pageview.IsVisualEditorMode) { 619 RatioSettings ratioSettings = GetRatioSettings("desktop"); 620 621 <div class="h-100 @theme"> 622 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 623 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 624 </div> 625 </div> 626 } 627 } 628
Error executing template "Designs/Swift/Paragraph/Swift_ProductPrice.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_a60a88e6edf544f096475964d9844581.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 22 bool anonymousUser = Pageview.User == null; 23 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 24 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 25 26 bool productIsDiscontinued = product is object && product.Discontinued; 27 bool doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued"); 28 var isDiscontinued = productIsDiscontinued && doNotShowPriceIfProductIsDiscontinued; 29 } 30 31 @if (product is object && !hidePrice && !isDiscontinued) { 32 bool showInformativePrice = Model.Item.GetBoolean("ShowInformativePrice"); 33 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 34 35 string priceFontSize = Model.Item.GetRawValueString("PriceSize", "fs-2"); 36 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 37 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 38 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 39 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 40 41 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 42 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 43 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 44 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 45 46 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 47 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 48 string order = layout == "horizontal" ? string.Empty : "order-2"; 49 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 50 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 51 52 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 53 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 54 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 55 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 56 57 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 58 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 59 60 string priceMin = ""; 61 string priceMax = ""; 62 63 string liveInfoClass = ""; 64 string productInfoFeed = ""; 65 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 66 if (isLazyLoadingForProductInfoEnabled) 67 { 68 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 69 { 70 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 71 if (!string.IsNullOrEmpty(productInfoFeed)) 72 { 73 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 74 } 75 } 76 liveInfoClass = "js-live-info"; 77 } 78 79 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 80 @if (showInformativePrice && product.PriceInformative.Price != 0) 81 { 82 <div class="opacity-50"> 83 <span>@Translate("RRP") </span> 84 <span class="text-decoration-line-through text-price">@product.PriceInformative.PriceFormatted</span> 85 </div> 86 } 87 <div class="@priceFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 88 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 89 90 91 @if (showPricesWithVat == "false" && !neverShowVat) 92 { 93 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 94 { 95 <span itemprop="price" content="" class="d-none"></span> 96 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 97 } 98 else 99 { 100 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVatFormatted : product.PriceBeforeDiscount.PriceWithoutVatFormatted; 101 102 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 103 if (product.Price.Price != product.PriceBeforeDiscount.Price) 104 { 105 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 106 } 107 } 108 } 109 else 110 { 111 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 112 { 113 <span itemprop="price" content="" class="d-none"></span> 114 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 115 } 116 else 117 { 118 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceFormatted : product.PriceBeforeDiscount.PriceFormatted; 119 120 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 121 122 if (product.Price.Price != product.PriceBeforeDiscount.Price) 123 { 124 <span class="text-decoration-line-through opacity-75 @order"> 125 <span class="text-price">@beforePrice</span> 126 </span> 127 } 128 } 129 } 130 131 @if (showPricesWithVat == "false" && !neverShowVat) 132 { 133 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 134 { 135 <span class="text-price js-text-price"> 136 <span class="spinner-border" role="status"></span> 137 </span> 138 } 139 else 140 { 141 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 142 143 if (product?.VariantInfo?.VariantInfo != null) 144 { 145 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 146 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 147 } 148 if (priceMin != priceMax) 149 { 150 price = priceMin + " - " + priceMax; 151 } 152 <span class="@theme @contentPadding"> 153 <span class="text-price">@price</span> 154 </span> 155 } 156 } 157 else 158 { 159 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 160 { 161 <span class="text-price js-text-price"> 162 <span class="spinner-border" role="status"></span> 163 </span> 164 } 165 else 166 { 167 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 168 169 if (product?.VariantInfo?.VariantInfo != null) 170 { 171 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 172 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 173 } 174 if (priceMin != priceMax) 175 { 176 price = priceMin + " - " + priceMax; 177 } 178 <span class="@theme @contentPadding"> 179 <span class="text-price">@price</span> 180 </span> 181 } 182 } 183 184 @* Stock state for Schema.org, start *@ 185 @{ 186 Uri url = Dynamicweb.Context.Current.Request.Url; 187 } 188 189 <link itemprop="url" href="@url"> 190 191 @{ 192 bool IsNeverOutOfStock = product.NeverOutOfstock; 193 } 194 195 @if (IsNeverOutOfStock) 196 { 197 <span itemprop="availability" class="d-none">@Translate("Available in stock")</span> 198 } 199 else 200 { 201 if (product.StockLevel > 0) 202 { 203 <span itemprop="availability" class="d-none">InStock</span> 204 } 205 else 206 { 207 <span itemprop="availability" class="d-none">OutOfStock</span> 208 } 209 } 210 @* Stock state for Schema.org, stop *@ 211 212 </div> 213 214 @if (showPricesWithVat == "false" && !neverShowVat) 215 { 216 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 217 { 218 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 219 } 220 else 221 { 222 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVatFormatted : product.Price.PriceWithVatFormatted; 223 224 if (product?.VariantInfo?.VariantInfo != null) 225 { 226 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 227 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 228 } 229 if (priceMin != priceMax) 230 { 231 price = priceMin + " - " + priceMax; 232 } 233 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 234 } 235 } 236 </div> 237 } 238 else if (Pageview.IsVisualEditorMode) 239 { 240 <div class="alert alert-dark m-0" role="alert"> 241 <span>@Translate("No products available")</span> 242 </div> 243 } 244
Error executing template "Designs/Swift/Paragraph/Swift_ProductAddToCart.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_f81b461ef4c2498fb57cb93c2462b1f7.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Core.Encoders 4 @using System.Globalization 5 6 @functions { 7 string DoubleToString(double? value) 8 { 9 if (value.HasValue) 10 { 11 return value.Value.ToString(CultureInfo.InvariantCulture); 12 } 13 return null; 14 } 15 } 16 17 @{ 18 ProductViewModel product = null; 19 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 20 { 21 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 22 } 23 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 24 { 25 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 26 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 27 28 if (productList?.Products is object) 29 { 30 product = productList.Products[0]; 31 } 32 } 33 34 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 35 bool anonymousUser = Pageview.User == null; 36 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 37 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 38 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 39 } 40 41 @if (product is object && !hideAddToCart) 42 { 43 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 44 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 45 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 46 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 47 48 bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; 49 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; 50 bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; 51 bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; 52 bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; 53 54 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 55 string inputSize = string.Empty; 56 57 switch (buttonSize) 58 { 59 case "small": 60 inputSize = " input-group-sm"; 61 buttonSize = " btn-sm"; 62 break; 63 case "regular": 64 buttonSize = string.Empty; 65 break; 66 case "large": 67 inputSize = " input-group-lg"; 68 buttonSize = " btn-lg"; 69 break; 70 } 71 72 string iconPath = "/Files/icons/"; 73 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 74 if (!url.Contains("LayoutTemplate")) 75 { 76 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 77 } 78 79 string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); 80 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 81 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 82 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 83 string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; 84 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 85 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? $"<span class=\"d-none d-md-inline\">{Translate("Add to cart")}</span><span class=\"d-inline d-md-none\">{Translate("Add")}</span>" : ""; 86 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 87 88 bool userHasPendingQuote = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.IsQuote; 89 90 if (product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable") 91 { 92 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 93 if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) 94 { 95 if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) 96 { 97 unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; 98 } 99 } 100 101 double? stepQty = product.PurchaseQuantityStep > 0 ? product.PurchaseQuantityStep : 1; 102 double? minQty = product.PurchaseMinimumQuantity > 0 ? product.PurchaseMinimumQuantity : 1; 103 double? valueQty = minQty > stepQty ? minQty : stepQty; 104 string disableAddToCart = null; 105 double? maxQty = null; 106 107 if (product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock && !product.NeverOutOfstock) 108 { 109 disableAddToCart = (product.StockLevel <= 0) || (!product.NeverOutOfstock && isLazyLoadingForProductInfoEnabled) ? "disabled" : disableAddToCart; 110 maxQty = product.StockLevel; 111 } 112 113 disableAddToCart = whenVariantsExist == "disable" && product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId) ? "disabled" : disableAddToCart; 114 disableAddToCart = product.Discontinued ? "disabled" : disableAddToCart; 115 116 if (unitsSelector && product.UnitOptions.Count > 0) 117 { 118 <form method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID"> 119 <input type="hidden" name="redirect" value="false"> 120 <input type="hidden" name="VariantID" value="@product.VariantId"> 121 <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> 122 </form> 123 } 124 125 <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> 126 @if (!anonymousUser && favoritesSelector) 127 { 128 @RenderPartial("Components/ToggleFavorite.cshtml", product) 129 } 130 131 <form method="post" action="@url" class="@fullWidth" style="z-index: 1"> 132 <input type="hidden" name="redirect" value="false"> 133 <input type="hidden" name="ProductId" value="@product.Id"> 134 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 135 <input type="hidden" name="ProductVariantName" value="@product.VariantName"> 136 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 137 <input type="hidden" name="ProductPrice" value="@PriceViewModelExtensions.ToStringInvariant(product.Price)"> 138 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 139 <input type="hidden" name="cartcmd" value="add"> 140 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 141 142 @if (!string.IsNullOrEmpty(product.VariantId)) 143 { 144 <input type="hidden" name="VariantId" value="@product.VariantId"> 145 } 146 147 <template class="js-step-quantity-warning"> 148 <div class="modal-header"> 149 <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> 150 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 151 </div> 152 <div class="modal-body"> 153 @Translate("Please select a quantity that is dividable by") @stepQty 154 </div> 155 </template> 156 157 158 <template class="js-min-quantity-warning"> 159 <div class="modal-header"> 160 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 161 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 162 </div> 163 <div class="modal-body"> 164 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 165 </div> 166 </template> 167 168 <template class="js-value-missing-warning"> 169 <div class="modal-header"> 170 <h1 class="modal-title fs-5">@Translate("No amount specified")</h1> 171 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 172 </div> 173 <div class="modal-body"> 174 @Translate("Specify an amount to add to the cart") 175 </div> 176 </template> 177 178 179 @if (userHasPendingQuote) 180 { 181 <input type="hidden" name="PendingQuote" value="true"> 182 183 <template class="js-pending-quote-notice"> 184 <div class="modal-header"> 185 <h1 class="modal-title fs-5">@Translate("Pending Quote")</h1> 186 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 187 </div> 188 <div class="modal-body"> 189 @Translate("You need to complete your current quote or empty the cart before adding this product to cart.") 190 </div> 191 </template> 192 } 193 194 @if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 195 { 196 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 197 } 198 199 <div class="d-flex flex-row w-100"> 200 @if (!quantitySelector) 201 { 202 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 203 } 204 205 @if (unitsSelector && product.UnitOptions.Count > 0) 206 { 207 string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; 208 209 foreach (var unitOption in product.UnitOptions) 210 { 211 if (unitOption.Id == unitId) 212 { 213 selectedUnitName = unitOption.Name; 214 } 215 } 216 217 <div class="d-flex flex-column gap-2 w-100"> 218 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 219 220 @if (quantitySelector) 221 { 222 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@DoubleToString(valueQty)" step="@DoubleToString(stepQty)" min="@DoubleToString(minQty)" max="@DoubleToString(maxQty)" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 223 } 224 225 <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> 226 @selectedUnitName 227 </button> 228 229 <ul class="dropdown-menu swift_unit-field"> 230 @foreach (var unitOption in product.UnitOptions) 231 { 232 var selectedUnit = unitOption.Id == unitId ? "selected" : ""; 233 234 <li> 235 <button type="button" class="btn dropdown-item" data-value="@unitOption.Id" onclick="document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID').querySelector('.js-unit-id').value = this.getAttribute('data-value'); 236 document.querySelector('#Unit_@(product.Id)_@product.VariantId.Replace(".", "_")').value = this.getAttribute('data-value'); 237 swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID'))"> 238 <span>@unitOption.Name</span> 239 <span> 240 @if (unitOption.StockLevel > 0 || unitOption.NeverOutOfStock) 241 { 242 if (!Model.Item.GetBoolean("HideInventory") && !unitOption.NeverOutOfStock) 243 { 244 <span class="small text-success">@unitOption.StockLevel @Translate("In stock")</span> 245 } 246 else 247 { 248 <span class="small text-success">@Translate("In stock")</span> 249 } 250 } 251 else 252 { 253 <span class="small text-danger">@Translate("Out of Stock")</span> 254 } 255 </span> 256 </button> 257 </li> 258 } 259 </ul> 260 </div> 261 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 262 @if (!Model.Item.GetBoolean("HideButtonText")) 263 { 264 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 265 @addToCartLabel 266 </span> 267 } 268 else 269 { 270 @addToCartLabel 271 } 272 </button> 273 </div> 274 } 275 else 276 { 277 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 278 @if (quantitySelector) 279 { 280 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@DoubleToString(valueQty)" step="@DoubleToString(stepQty)" min="@DoubleToString(minQty)" max="@DoubleToString(maxQty)" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 281 } 282 283 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 284 @if (!Model.Item.GetBoolean("HideButtonText")) 285 { 286 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 287 @addToCartLabel 288 </span> 289 } 290 else 291 { 292 @addToCartLabel 293 } 294 </button> 295 </div> 296 } 297 </div> 298 </form> 299 </div> 300 } 301 else if (whenVariantsExist == "modal") 302 { 303 string ButtonShape = Model.Item.GetRawValueString("VariantButtonShape", "square"); 304 string buttonAspectRatio = Model.Item.GetRawValueString("VariantImageAspectRatio", "56%"); 305 306 string buttonText = Translate("Select"); 307 string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; 308 309 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 310 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 311 312 <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> 313 @if (!anonymousUser && favoritesSelector) 314 { 315 @RenderPartial("Components/ToggleFavorite.cshtml", product) 316 } 317 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> 318 <input type="hidden" name="ProductID" value="@product.Id"> 319 <input type="hidden" name="VariantID" value="@variantId"> 320 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 321 <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> 322 <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> 323 <input type="hidden" name="ButtonLayout" value="@ButtonShape"> 324 <input type="hidden" name="ButtonAspectRatio" value="@buttonAspectRatio"> 325 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 326 <input type="hidden" name="ViewType" value="ModalContent"> 327 @if (isLazyLoadingForProductInfoEnabled) 328 { 329 @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ 330 <input type="hidden" name="getproductinfo" value="true"> 331 } 332 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 333 </form> 334 </div> 335 } 336 } 337 else if (Pageview.IsVisualEditorMode) 338 { 339 <div class="alert alert-dark m-0">@Translate("No products available")</div> 340 } 341
Ikke på lager
Suppler med:
Hvorfor skal du handle på troelstrup.com?
Få fri fragt
30 dages returret
Bliv en del af Troelstrup Exclusive
Beskrivelse
Yderligere information
Detaljer
Specifikationer
Om brandet
Lign. produkter
Error executing template "Designs/Swift/Paragraph/Swift_ProductComponentSlider.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_1ccb3af0977a43e5b54be2179c3c8f76.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string layout = Model.Item.GetRawValueString("Layout") == "slider" ? "ProductSliderComponent" : "ProductGridComponent"; 23 string lazyHeight = Model.Item.GetBoolean("SetMinHeightForLazyLoading") ? "min-height: 360px" : ""; 24 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 25 26 //Link generation 27 string pageId = Model.Item.GetLink("ProductSliderServicePage") != null ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 28 string servicePageByNavigationTag = GetPageIdByNavigationTag("ProductSliderService") != 0 ? GetPageIdByNavigationTag("ProductSliderService").ToString() : ""; 29 pageId = pageId == "" ? servicePageByNavigationTag : pageId; 30 31 string url = "/Default.aspx?ID=" + pageId; 32 33 if (Pageview.IsVisualEditorMode) 34 { 35 url += "&VisualEdit=True"; 36 } 37 38 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 39 if (isLazyLoadingForProductInfoEnabled) 40 { 41 url += "&getproductinfo=true"; 42 } 43 44 url += $"&ProductListPartial={layout}"; //Use this to render either a slider or a grid 45 46 //Source type 47 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 48 IList<string> relateFromProductIds = new List<string> { }; 49 IList<string> relateFromProductVariantIds = new List<string> { }; 50 IList<string> relateFromGroupIds = new List<string> { }; 51 bool hasVariants = false; 52 53 54 ProductListViewModel relateToViewModel = Model.Item.GetValue<ProductListViewModel>("RelateTo"); 55 56 //--- PRODUCTS --- 57 if (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected") 58 { 59 if (relateToViewModel?.Products != null) 60 { 61 hasVariants = relateToViewModel.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 62 foreach (var fromProduct in relateToViewModel.Products) 63 { 64 if (hasVariants) 65 { 66 if (!string.IsNullOrEmpty(fromProduct.VariantId)) 67 { 68 relateFromProductVariantIds.Add($"{fromProduct.Id} {fromProduct.VariantId}"); 69 } 70 else 71 { 72 relateFromProductVariantIds.Add($"{fromProduct.Id}"); 73 } 74 } 75 relateFromProductIds.Add($"{fromProduct.Id}"); 76 } 77 } 78 } 79 80 //--- GROUPS --- 81 if (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest" || sourceType == "custom") 82 { 83 var groupsToRelateTo = Model.Item.GetList("RelateTo")?.GetRawValue().OfType<string>().ToList(); 84 85 if (groupsToRelateTo != null) { 86 foreach (var fromGroup in groupsToRelateTo) 87 { 88 var groupId = fromGroup.Length > 2 ? fromGroup.Remove(0, 2) : fromGroup; 89 90 if (!fromGroup.Contains("p_")) 91 { 92 relateFromGroupIds.Add(groupId); 93 } 94 } 95 } 96 97 if (relateToViewModel?.Products != null) 98 { 99 foreach (var fromProduct in relateToViewModel.Products) 100 { 101 var groupId = fromProduct.PrimaryOrDefaultGroup.Id; 102 relateFromGroupIds.Add(groupId); 103 } 104 } 105 } 106 107 string relationGroupId = Model.Item.GetRawValueString("RelationGroup", string.Empty); 108 109 //--- RELATED GROUP --- 110 if (sourceType == "related") 111 { 112 if (relateToViewModel?.Products != null) 113 { 114 product = relateToViewModel.Products.FirstOrDefault(); 115 } 116 117 if (product?.RelatedGroups != null) 118 { 119 foreach (var relationGroup in product.RelatedGroups) 120 { 121 if (relationGroup.Id == relationGroupId) { 122 hasVariants = relationGroup.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 123 foreach (var fromProduct in relationGroup.Products) 124 { 125 if (hasVariants) 126 { 127 if (!string.IsNullOrEmpty(fromProduct.VariantId)) 128 { 129 relateFromProductVariantIds.Add($"{fromProduct.ProductId} {fromProduct.VariantId}"); 130 } 131 else 132 { 133 relateFromProductVariantIds.Add($"{fromProduct.ProductId}"); 134 } 135 } 136 relateFromProductIds.Add($"{fromProduct.ProductId}"); 137 } 138 } 139 } 140 } 141 } 142 143 144 //Create group id collection and products id collection strings 145 string productIds = relateFromProductIds.Count > 0 ? string.Join(",", relateFromProductIds) : ""; 146 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 147 string groupIds = relateFromGroupIds.Count > 0 ? string.Join(",", relateFromGroupIds) : ""; 148 149 if (product is object) 150 { 151 if (string.IsNullOrEmpty(productIds) && (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected" || sourceType == "custom")) 152 { 153 productIds = product.Id; 154 } 155 156 if (string.IsNullOrEmpty(groupIds) && (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest")) 157 { 158 groupIds = product.PrimaryOrDefaultGroup.Id; 159 } 160 } 161 } 162 163 @*Container element for the request*@ 164 @if (!string.IsNullOrEmpty(productIds) || !string.IsNullOrEmpty(groupIds) || (string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(groupIds) && sourceType != "related")) 165 { 166 <form method="post" action="@url" id="ComponentSliderProductsForm_@Model.ID" data-response-target-element="ComponentSliderProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 167 <input type="hidden" name="ParagraphId" value="@Model.ID" /> 168 <input type="hidden" name="SortOrder" value="DESC"> 169 <input type="hidden" name="SourceType" value="@sourceType"> 170 171 @if (!string.IsNullOrEmpty(groupIds)) 172 { 173 <input type="hidden" name="GroupId" value="@groupIds"> 174 } 175 @if (sourceType != "frequently" && !string.IsNullOrEmpty(productIds) && !hasVariants) 176 { 177 <input type="hidden" name="MainProductId" value="@productIds"> 178 } 179 @if (sourceType != "frequently" && hasVariants) 180 { 181 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 182 } 183 184 @if (Model.Item.GetInt32("ProductsCount") != 0) 185 { 186 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 187 } 188 189 @if (sourceType == "variants") 190 { 191 <input type="hidden" name="isVariant" value="true"> 192 } 193 @if (sourceType == "most-sold") 194 { 195 <input type="hidden" name="SortBy" value="OrderCount"> 196 } 197 @if (sourceType == "trending") 198 { 199 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 200 } 201 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 202 { 203 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 204 } 205 @if (sourceType == "latest") 206 { 207 <input type="hidden" name="SortBy" value="Created"> 208 } 209 </form> 210 211 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 212 <script> 213 window.addEventListener("load", () => { 214 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 215 }); 216 </script> 217 218 <div class="w-100 h-100@(theme)"> 219 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 220 <div id="ComponentSliderProducts_@Model.ID" class="h-100" style="@lazyHeight"></div> 221 </div> 222 223 <script type="module"> 224 const productSliderContainer = document.querySelector("#ComponentSliderProducts_@Model.ID"); 225 const productSliderForm = document.querySelector("#ComponentSliderProductsForm_@Model.ID"); 226 227 swift.PageUpdater.Update(productSliderForm); 228 productSliderForm.addEventListener("updated.swift.pageupdater", function(e){ 229 if (e.detail.html === "") { 230 productSliderContainer.closest("[data-col-size]").classList.add("d-none"); 231 } 232 }); 233 </script> 234 } 235 else if (Pageview.IsVisualEditorMode == true) 236 { 237 <div class="alert alert-dark" role="alert"> 238 <span>@Translate("Product component slider: The slider will be rendered here, if there is anything to show")</span> 239 </div> 240 } 241
Beklager. Der er ikke noget at se her