Tamron objektiver
Error executing template "/Designs/Swift/Paragraph/Swift_ProductListGridView_Custom.cshtml" System.InvalidCastException: Unable to cast object of type 'System.DBNull' to type 'System.String'. at NLWI.Platforms.Dynamicweb9.Specs.Repositories.ProductSpecificationRepository.GetSpecificationObjectIdentifiers(HashSet`1 autoIds) at NLWI.Platforms.Dynamicweb9.Specs.Repositories.ProductSpecificationRepository.GetProductAutoIdMap(HashSet`1 autoIds) at NLWI.Platforms.Dynamicweb9.Specs.Services.CachedProductSpecificationService.<GetAutoIdMap>b__14_1(HashSet`1 k) at NLWI.Core.Platform.Caching.DictionaryCaching.GetOrInsert[TKey,TVal](HashSet`1 keys, Func`2 buildCacheKey, Func`2 lookupMethod, CacheOptions cacheOptions) at NLWI.Platforms.Dynamicweb9.Specs.Services.CachedProductSpecificationService.GetAutoIdMap(HashSet`1 productAutoIds) at NLWI.Platforms.Dynamicweb9.Specs.Services.CachedProductSpecificationService.GetProductSpecifications(HashSet`1 productAutoIds) at NLWI.Platforms.Dynamicweb9.Specs.Services.CachedProductSpecificationService.GetProductSpecifications(Int64 productAutoId) at CompiledRazorTemplates.Dynamic.RazorEngine_dfcffeb1681942f4b6ce2dcd36fefbb6.<RenderProductList>b__10_0(TextWriter __razor_helper_writer) in C:\inetpub\solutions\Goecker-2022-Staging\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_Custom.cshtml:line 298 at CompiledRazorTemplates.Dynamic.RazorEngine_dfcffeb1681942f4b6ce2dcd36fefbb6.Execute() in C:\inetpub\solutions\Goecker-2022-Staging\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_Custom.cshtml:line 190 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) 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.Ecommerce.CustomerExperienceCenter.Favorites 4 @using System.Linq 5 @using Dynamicweb.Core 6 @using Dynamicweb.Environment 7 @using NLWI.Core.Factory 8 @using NLWI.Platforms.Dynamicweb9.Specs.ViewModels 9 @using NLWI.Platforms.Dynamicweb9.Specs.Services 10 @using NORRIQ.CustomCode.Razor 11 @using NORRIQ.CustomCode.StockLocations 12 @using System.Text; 13 @using System.Web; 14 @using Dynamicweb; 15 @using Dynamicweb.Core.Encoders 16 @using System.Globalization 17 18 @* CUSTOMIZED STANDARD SWIFT (v1.15.0) TEMPLATE *@ 19 20 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 21 22 @helper RenderAddedModal(bool isRental = false) 23 { 24 string productsFeedUrl = $"/Default.aspx?ID={GetPageIdByNavigationTag("productsfeed")}&feed=true&redirect=false"; 25 string checkoutUrl = isRental ? $"/Default.aspx?ID={GetPageIdByNavigationTag("rentalcheckout")}" : $"/Default.aspx?ID={GetPageIdByNavigationTag("checkout")}"; 26 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 27 if (!cartUrl.Contains("LayoutTemplate")) 28 { 29 cartUrl += cartUrl.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 30 } 31 32 <div class="modal fade product-added-modal js-product-added-modal" data-productsfeedurl="@productsFeedUrl" tabindex="-1" role="dialog"> 33 <input type="hidden" class="js-translation choose" value="@HttpUtility.HtmlEncode(Translate("Smartpage.Product.AddedModal.Choose", "Vælg"))" /> 34 <input type="hidden" class="js-translation period-price" value="@HttpUtility.HtmlAttributeEncode(Translate("Smartpage:Rental.PeriodPrice", "Pris for periode:"))" /> 35 <input type="hidden" class="js-translation daily-price" value="@HttpUtility.HtmlAttributeEncode(Translate("Smartpage:Rental.DailyPrice", "Dagspris:"))" /> 36 37 <div class="modal-dialog" role="document"> 38 <div class="modal-content"> 39 <div class="modal-header"> 40 <div class="d-flex flex-column"> 41 <h3 class="modal-title">@Translate("Smartpage.Product.AddedModal.Header", "Tilføjet til kurv")</h3> 42 @if (isRental) 43 { 44 <p> 45 @Translate("Smartpage.Rental.PeriodDays", "Skydedage") 46 <span class="js-rental-periodfrom"></span> - <span class="js-rental-periodto"></span> 47 </p> 48 } 49 </div> 50 <div class="cross-icon cursor-pointer" onclick="hideModal(this)"></div> 51 </div> 52 <div class="spinner-border js-loader powerstep-loader text-dark d-none"> 53 <span class="visually-hidden">Loading..</span> 54 </div> 55 <div class="modal-body container pt-2 pb-0 px-3 m-0 text-center text-lg-start"> 56 <div class="js-product-modal-orderline row gap-2 gap-lg-0 d-flex flex-column flex-lg-row justify-content-between pb-2 border-bottom"> 57 <div class="col-lg-6 col-12 d-flex align-items-center flex-column flex-lg-row gap-3 gap-lg-0"> 58 <img class="js-product-modal-orderline-image mr-2" /> 59 <a class="js-product-modal-orderline-name text-decoration-none fw-bold px-md-3 px-lg-0"> 60 </a> 61 </div> 62 <div class="col-lg-6 col-12 d-flex flex-column align-items-center align-items-lg-end justify-content-center"> 63 @if (!isRental) 64 { 65 <div class="d-flex mr-2"> 66 <span class="js-product-modal-orderline-price"></span> 67 </div> 68 } 69 else 70 { 71 <div class="d-flex mr-2"> 72 <span class="me-2">@Translate("Smartpage:Rental.DailyPrice", "Dagspris:")</span> <span class="js-product-modal-orderline-dailyprice"></span> 73 </div> 74 <div class="d-flex"> 75 <span class="me-2">@Translate("Smartpage:Rental.PeriodPrice", "Pris for periode:")</span> <span class="js-product-modal-orderline-periodprice"></span> 76 </div> 77 } 78 </div> 79 </div> 80 <div class="row border-bottom"> 81 <h5 class="fw-bold py-3 fs-sm-7 fs-md-6">@Translate("Smartpage.Product.AddedModal.Recommendation", "Til ovenstående produkt anbefaler vi:")</h5> 82 </div> 83 <div class="js-product-modal-relatedproducts d-flex flex-column"></div> 84 </div> 85 86 <div class="modal-footer border-top-0 justify-content-end"> 87 @if (!isRental) 88 { 89 <form method="post" action="@cartUrl" class="js-relatedproducts-form"> 90 <input type="hidden" name="cartcmd" value="addmulti" /> 91 <input type="hidden" name="redirect" value="false" /> 92 93 <div class="js-products-inputs"></div> 94 95 <button class="btn btn-primary js-add-to-cart-button disabled" onclick="swift.Cart.Update(event)">@Translate("Tilføj til kurv")</button> 96 </form> 97 } 98 else 99 { 100 <form class="js-relatedproducts-form"> 101 <div class="js-products-inputs"></div> 102 <button class="btn btn-primary js-add-to-rental disabled" onclick="addRelatedRentalProducts(event, this)">@Translate("Tilføj til kurv")</button> 103 </form> 104 } 105 </div> 106 <div class="modal-footer justify-content-between"> 107 <button type="button" class="btn btn-primary" onclick="hideModal(this)">@Translate("Smartpage.Product.AddedModal.BackToShop", "Tilbage til shop")</button> 108 <a href="@checkoutUrl" class="btn btn-primary">@Translate("Smartpage.Product.AddedModal.Checkout", "Til checkout")</a> 109 </div> 110 </div> 111 </div> 112 </div> 113 } 114 @using NLWI.Platforms.Dynamicweb9.Specs.ViewModels 115 116 @functions { 117 string GetProductImage(ProductSpecifications specs) 118 { 119 string imageUrl = "/Files/Images/Perfion/default.png"; 120 121 if (specs.HasKey("Primærbillede")) 122 { 123 imageUrl = specs.GetByKey("Primærbillede").Value; 124 } 125 126 return imageUrl; 127 } 128 129 Dictionary<string, string> GetAllProductImages(ProductSpecifications specs) 130 { 131 Dictionary<string, string> images = new Dictionary<string, string>(); 132 133 foreach (var img in specs.GetByGroup("Billeder")) 134 { 135 if (!images.ContainsKey(img.Value) && img.Key != "Brandlogo") 136 { 137 images.Add(img.Value, img.Caption); 138 } 139 } 140 141 return images; 142 } 143 } 144 145 @functions 146 { 147 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled; 148 string liveInfoClass = ""; 149 string productInfoFeed = ""; 150 151 string showPricesWithVat = ""; 152 bool neverShowVat = false; 153 154 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")); 155 156 ProductListViewModel productList = new ProductListViewModel(); 157 } 158 159 @{ 160 if (Dynamicweb.Context.Current.Items.Contains("ProductList")) 161 { 162 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"]; 163 } 164 165 showPricesWithVat = HttpContext.Current.Session["Smartpage:ShowPricesWithVat"] != null ? Convert.ToString(HttpContext.Current.Session["Smartpage:ShowPricesWithVat"]) : Pageview.Area.EcomPricesWithVat.ToLower(); 166 neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 167 168 if (isLazyLoadingForProductInfoEnabled) 169 { 170 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 171 { 172 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 173 if (!string.IsNullOrEmpty(productInfoFeed)) 174 { 175 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 176 } 177 } 178 liveInfoClass = "js-live-info"; 179 } 180 181 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 182 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 183 } 184 185 @if (!isDetailPage) 186 { 187 if (!string.IsNullOrEmpty(theme)) 188 { 189 <div class="h-100@(theme) @themePadding item_@Model.Item.SystemName.ToLower()" @productInfoFeed> 190 @RenderProductList() 191 </div> 192 } 193 else 194 { 195 <div class="pt-3 item_@Model.Item.SystemName.ToLower()" @productInfoFeed> 196 @RenderProductList() 197 </div> 198 } 199 } 200 201 @helper RenderProductList() 202 { 203 var productSpecificationService = ObjectFactory.GetInstance<IProductSpecificationService>(); 204 var stockService = ObjectFactory.GetInstance<StockService>(); 205 var featurePostFix = Pageview.AreaSettings.GetItem("CustomSettings").GetRawValueString("FeaturePostFix_Custom", ""); 206 207 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 208 bool anonymousUser = Pageview.User == null; 209 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 210 211 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : ""; 212 string productThemePadding = productTheme != string.Empty ? "p-3" : string.Empty; 213 214 string url = Dynamicweb.Context.Current.Request.RawUrl; 215 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 216 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Model.Item.GetString("ShowFavoritesSelectorMasterProduct")) ? Model.Item.GetBoolean("ShowFavoritesSelectorMasterProduct") : false; 217 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide"); 218 219 string groupId = productList?.Group?.Id != null ? productList.Group.Id : ""; 220 221 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 222 if (!cartUrl.Contains("LayoutTemplate")) 223 { 224 cartUrl += cartUrl.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 225 } 226 227 var badgeParms = new Dictionary<string, object>(); 228 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 229 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 230 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 231 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 232 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 233 234 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 235 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 236 237 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID"); 238 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 239 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 240 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 241 242 var favoriteParameters = new Dictionary<string, object>(); 243 if (!anonymousUser && !hideFavoritesSelector) 244 { 245 int defaultFavoriteListId = 0; 246 247 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 248 if (favoreiteLists.Count() == 1) 249 { 250 foreach (FavoriteList list in favoreiteLists) 251 { 252 defaultFavoriteListId = list.ListId; 253 } 254 } 255 256 favoriteParameters.Add("ListId", defaultFavoriteListId); 257 } 258 259 if (productList.TotalProductsCount > 0) 260 { 261 int pageSizeSetting = 30; 262 int pageSize = productList.PageSize; 263 pageSize += pageSizeSetting; 264 265 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize; 266 267 <div class="grid grid-2 grid-lg-3"> 268 @* 269 20250512: Removed by AE 270 @RenderAddedModal(false) 271 *@ 272 273 <script> 274 window.addEventListener('CookieInformationConsentGiven', function (event) { 275 if (CookieInformation.getConsentGivenFor('cookie_cat_statistic')) { 276 gtag("event", "view_item_list", { 277 item_list_id: "product_list_gridview", 278 item_list_name: "Product list (Gridview)", 279 items: [ 280 @foreach (ProductViewModel product in productList.Products) 281 { 282 <text>{ 283 item_id: "@product.Number", 284 item_name: "@product.Name", 285 currency: "@product.Price.CurrencyCode", 286 price: @product.Price.Price 287 },</text> 288 } 289 ] 290 }); 291 } 292 }, false); 293 </script> 294 295 296 @foreach (ProductViewModel product in productList.Products) 297 { 298 var specs = product.GetSpecifications(); 299 var availableStockLocations = product.StockUnits.Where(a => a.StockLevel > 0).Select(a => a.StockLocationName).ToList(); 300 var inStock = availableStockLocations.Any(); 301 302 string variantIdForLink = !string.IsNullOrEmpty(product.VariantId) ? $"&VariantID={product.VariantId}" : ""; 303 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(product.DefaultVariantId) ? $"&VariantID={product.DefaultVariantId}" : variantIdForLink; 304 305 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 306 link += $"&GroupID={product.PrimaryOrDefaultGroup.Id}"; 307 link += $"&ProductID={product.Id}"; 308 link += variantIdForLink; 309 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 310 311 string imagePath = product?.DefaultImage?.Value ?? ""; 312 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 313 314 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 315 ratio = ratio != "0" ? ratio : ""; 316 string ratioCssClass = ratio != "" ? " ratio" : ""; 317 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 318 319 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp"; 320 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp"; 321 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp"; 322 323 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 324 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 325 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty; 326 327 string imageId = "ProductImage_" + product.Id + product.VariantId; 328 string priceId = "ProductPrice_" + product.Id + product.VariantId; 329 330 @* Alternative image *@ 331 var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" }; 332 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 333 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets"); 334 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 335 336 if (alternativeImagesList.FirstOrDefault() != null) 337 { 338 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage)); 339 340 if (alternativeImagesList.First().Value == defaultImage) 341 { 342 alternativeImagesList = alternativeImagesList.Skip(1); 343 } 344 } 345 346 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : ""; 347 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : ""; 348 349 @* Badges *@ 350 DateTime createdDate = product.Created.Value; 351 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 352 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 353 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 354 355 @* Main features *@ 356 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 357 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 358 359 foreach (var selection in selectedDisplayGroups) 360 { 361 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 362 { 363 if (selection == group.Id) 364 { 365 mainFeatures.Add(group); 366 } 367 } 368 } 369 370 //CUSTOM 371 var disablePrice = product.ProductFields.ContainsKey("Custom_DisablePrice") ? Convert.ToBoolean(product.ProductFields["Custom_DisablePrice"].Value) : false; 372 var disablePurchase = product.ProductFields.ContainsKey("Custom_DisablePurchase") ? Convert.ToBoolean(product.ProductFields["Custom_DisablePurchase"].Value) : false; 373 var disableStock = product.ProductFields.ContainsKey("Custom_DisableStock") ? Convert.ToBoolean(product.ProductFields["Custom_DisableStock"].Value) : false; 374 var enableContact = product.ProductFields.ContainsKey("Custom_EnableContact") ? Convert.ToBoolean(product.ProductFields["Custom_EnableContact"].Value) : false; 375 var enableBookDemo = product.ProductFields.ContainsKey("Custom_EnableBookDemo") ? Convert.ToBoolean(product.ProductFields["Custom_EnableBookDemo"].Value) : false; 376 377 var productDiscountInfo = Custom.Discounts.Services.DiscountInfoService.GetDiscountExtenderInfoByProductId(product.Id)?.Where(x => !string.IsNullOrEmpty(x.CampaignLabel)); 378 //--CUSTOM 379 380 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id" itemscope itemtype="https://schema.org/Product"> 381 382 @if (specs.HasKey("Ikon" + featurePostFix) 383 || (productDiscountInfo != null && productDiscountInfo.Any())) 384 { 385 <div class="product-label"> 386 @foreach (var label in specs.GetAllByKey("Ikon" + featurePostFix)) 387 { 388 var referenceSpecfication = label.GetReferenceSpecification(); 389 var txtcolor = "#ffffff"; 390 if (!string.IsNullOrEmpty(referenceSpecfication.GetByKey("LinkFarvekode").Value)) 391 { 392 txtcolor = referenceSpecfication.GetByKey("LinkFarvekode").Value; 393 } 394 if (referenceSpecfication.HasKey("LinkURL")) 395 { 396 <a href="@(referenceSpecfication.GetByKey("LinkURL").Value)" class="product-label-item" style="background-color:@(referenceSpecfication.GetByKey("Farvekode").Value);color:@txtcolor;"> 397 @referenceSpecfication.GetByKey("Label").Value 398 </a> 399 } 400 else 401 { 402 <span class="product-label-item" style="background-color:@(referenceSpecfication.GetByKey("Farvekode").Value);color:@txtcolor;"> 403 @referenceSpecfication.GetByKey("Label").Value 404 </span> 405 } 406 } 407 408 @*CUSTOM*@ 409 @if (productDiscountInfo != null) 410 { 411 foreach (var discount in productDiscountInfo) 412 { 413 <span class="product-label-item" style="background-color: @discount.CampaignLabelColor; color: @(!string.IsNullOrEmpty(discount.CampaignLabelTextColor) ? discount.CampaignLabelTextColor : "#ffffff");"> 414 @discount.CampaignLabel 415 </span> 416 } 417 } 418 @*CUSTOM*@ 419 420 </div> 421 } 422 423 @if (!anonymousUser) 424 { 425 if (!hideFavoritesSelector && product.VariantInfo.VariantInfo == null) 426 { 427 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2"> 428 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 429 </div> 430 } 431 else if (showFavoritesSelectorMasterProduct) 432 { 433 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2"> 434 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 435 </div> 436 } 437 } 438 439 @if (showBadges) 440 { 441 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2"> 442 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 443 </div> 444 } 445 446 <div class="d-flex flex-column d-block h-100 text-center"> 447 @{ 448 string clickProductLink = string.Empty; 449 if (allowTracking) 450 { 451 clickProductLink = "onclick=\"return clickProductLink('" + @product.Id + "', '" + @product.Name + "', '" + @product.VariantName + "', '" + @product.Price.CurrencyCode + "', '" + @product.Price.Price + "')\""; 452 } 453 } 454 <a href="@link" class="text-decoration-none d-flex flex-column" @clickProductLink> 455 @if (allowTracking) 456 { 457 <script> 458 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) { 459 if (typeof gtag !== "undefined") { 460 gtag("event", "select_item", { 461 item_list_id: "product_list_gridview", 462 item_list_name: "Product list (Gridview)", 463 items: [ 464 { 465 item_id: productId, 466 item_name: productName, 467 currency: productCurrency, 468 item_list_id: "product_list_gridview", 469 item_list_name: "Product list (Gridview)", 470 item_variant: productVariant, 471 price: productPrice 472 } 473 ] 474 }); 475 } 476 } 477 </script> 478 } 479 480 <div class="@productThemePadding order-2"> 481 <div class="flex-grow-1"> 482 <h3 class="h6 mb-0 text-break" itemprop="name"> 483 @product.Name @if (!string.IsNullOrEmpty(product.VariantName)) 484 {<text>(@product.VariantName)</text>} 485 </h3> 486 @if (!Model.Item.GetBoolean("HideProductNumber")) 487 { 488 <p class="fs-7 opacity-85 mb-2">@product.Number</p> 489 } 490 @if (mainFeatures.Count > 0) 491 { 492 <ul class="p-0 lh-sm opacity-75" style="list-style-position: inside"> 493 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 494 { 495 foreach (var field in mainFeatureGroup.Fields) 496 { 497 @RenderField(field.Value) 498 } 499 } 500 </ul> 501 } 502 </div> 503 </div> 504 505 <div class="overflow-hidden order-1 @(imageTheme)" @imageOutlineStyle> 506 <div class="ratio" style="@(ratioVariable)"> 507 <div class="d-flex justify-content-center align-items-center"> 508 @if (string.IsNullOrEmpty(alternativeImage)) 509 { 510 <img id="@imageId" itemprop="image" 511 srcset=" 512 @imagePathXs 480w, 513 @imagePathS 640w" 514 sizes="(min-width: 992px) 33vw, 50vw" 515 src="@imagePathFallBack" 516 loading="lazy" 517 decoding="async" 518 class="mw-100 mh-100 @imageThemePadding" 519 alt="@product.Name"> 520 } 521 else 522 { 523 <img id="@imageId" itemprop="image" 524 src="@imagePathFallBack" 525 loading="lazy" 526 decoding="async" 527 class="mw-100 mh-100 @imageThemePadding" 528 alt="@product.Name" 529 onmouseover="this.src='@alternativeImage'" 530 onmouseout="this.src='@imagePathFallBack'"> 531 } 532 </div> 533 </div> 534 535 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images") 536 { 537 int variantGroupCount = 0; 538 int showMaxVariantGroups = 2; 539 int showMaxVariants = 3; 540 var productVariantTheme = productTheme != "" ? productTheme : "bg-white"; 541 542 <div class="position-relative"> 543 <div id="StaticVariants_@product.Id" class="static-variants w-100 d-none d-lg-block position-absolute left-0 bottom-0 @productTheme" style="pointer-events: none;"> 544 545 @foreach (var variantGroup in product.VariantGroups()) 546 { 547 int variantsCount = 0; 548 549 <div class="d-flex gap-2 mb-2"> 550 @foreach (var variant in variantGroup.Options) 551 { 552 if (variantGroupCount < showMaxVariantGroups) 553 { 554 var optionsCount = variantGroup.Options.Count(); 555 556 if (variantsCount < showMaxVariants) 557 { 558 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : ""; 559 560 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;"> 561 @if (!string.IsNullOrEmpty(variant.Color)) 562 { 563 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value); 564 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value); 565 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 566 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 567 568 if (isLazyLoadingForProductInfoEnabled) 569 { 570 <figure class="w-100 d-block m-0" data-price-formatted="" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@defaultProductImage')"> 571 <div class="d-flex align-items-center justify-content-center"> 572 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name"> 573 </div> 574 </figure> 575 } 576 else 577 { 578 <figure class="w-100 d-block m-0" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@defaultPrice', '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@variantPrice', '@defaultProductImage')"> 579 <div class="d-flex align-items-center justify-content-center"> 580 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name"> 581 </div> 582 </figure> 583 } 584 } 585 else 586 { 587 <div class="d-flex align-items-center justify-content-center"> 588 @variant.Name 589 </div> 590 } 591 <div class="visually-hidden"> 592 <h4>@product.Name, @variant.Name</h4> 593 @if (!hidePrice) 594 { 595 if (isLazyLoadingForProductInfoEnabled) 596 { 597 <span class="text-price js-text-price"></span> 598 } 599 else 600 { 601 <span class="text-price">@product.Price.PriceFormatted</span> 602 } 603 } 604 </div> 605 </article> 606 } 607 608 variantsCount++; 609 610 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants) 611 { 612 int left = optionsCount - showMaxVariants; 613 <div class="variant-option ms-1 d-flex justify-content-center align-items-center"> 614 <span>+@left</span> 615 </div> 616 } 617 } 618 } 619 </div> 620 621 variantGroupCount++; 622 } 623 </div> 624 </div> 625 } 626 </div> 627 </a> 628 <div class="@productThemePadding mt-auto pt-0"> 629 630 @if (!disableStock) //CUSTOM 631 { 632 <div> 633 @if (inStock && availableStockLocations.Any()) 634 { 635 <span class="icon-stock icon-green position-relative" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="@HttpUtility.HtmlAttributeEncode(string.Join("<br />", availableStockLocations))" data-bs-html="true">@Translate("label_in_Stock", "På lager")</span> 636 } 637 else 638 { 639 <span class="icon-stock icon-red position-relative">@Translate("label_not_in_stock", "Ikke på lager")</span> 640 } 641 </div> 642 643 <script> 644 window.addEventListener('DOMContentLoaded', function () { 645 var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) 646 var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { 647 return new bootstrap.Tooltip(tooltipTriggerEl) 648 }) 649 }, false); 650 </script> 651 } 652 653 @if (!hidePrice && !disablePrice) //CUSTOM 654 { 655 string priceMin = ""; 656 string priceMax = ""; 657 658 <div class="mt-2"> 659 <div itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 660 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 661 662 @if (showPricesWithVat == "false" && !neverShowVat) 663 { 664 if (isLazyLoadingForProductInfoEnabled) 665 { 666 <span itemprop="price" content="" class="d-none"></span> 667 <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> 668 } 669 else 670 { 671 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 672 string informativePrice = product.PriceInformative.PriceWithoutVatFormatted; 673 674 <span itemprop="price" content="@product.Price.PriceWithoutVat.ToString("0.00", CultureInfo.InvariantCulture)" class="d-none"></span> 675 if (product.Price.Price != product.PriceBeforeDiscount.Price) 676 { 677 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> 678 } 679 else if (product.PriceInformative.Price > 0) 680 { 681 <span class="text-decoration-line-through opacity-75 me-3 text-price">@informativePrice</span> 682 } 683 } 684 } 685 else 686 { 687 if (isLazyLoadingForProductInfoEnabled) 688 { 689 <span itemprop="price" content="" class="d-none"></span> 690 <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> 691 } 692 else 693 { 694 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 695 string informativePrice = product.PriceInformative.PriceWithVatFormatted; 696 697 <span itemprop="price" content="@product.Price.Price.ToString("0.00", CultureInfo.InvariantCulture)" class="d-none"></span> 698 if (product.Price.Price != product.PriceBeforeDiscount.Price) 699 { 700 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> 701 } 702 else if (product.PriceInformative.Price > 0) 703 { 704 <span class="text-decoration-line-through opacity-75 me-3 text-price">@informativePrice</span> 705 } 706 } 707 } 708 709 @if (showPricesWithVat == "false" && !neverShowVat) 710 { 711 if (isLazyLoadingForProductInfoEnabled) 712 { 713 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 714 } 715 else 716 { 717 string price = product.Price.PriceWithoutVatFormatted; 718 if (product?.VariantInfo?.VariantInfo != null) 719 { 720 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 721 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 722 } 723 if (priceMin != priceMax) 724 { 725 price = priceMin + " - " + priceMax; 726 } 727 <span class="text-price">@price</span> 728 } 729 } 730 else 731 { 732 if (isLazyLoadingForProductInfoEnabled) 733 { 734 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 735 } 736 else 737 { 738 string price = product.Price.PriceFormatted; 739 if (product?.VariantInfo?.VariantInfo != null) 740 { 741 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 742 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 743 } 744 if (priceMin != priceMax) 745 { 746 price = priceMin + " - " + priceMax; 747 } 748 <span class="text-price">@price</span> 749 } 750 } 751 </div> 752 @if (showPricesWithVat == "false" && !neverShowVat) 753 { 754 if (isLazyLoadingForProductInfoEnabled) 755 { 756 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div> 757 } 758 else 759 { 760 string price = product.Price.PriceWithVatFormatted; 761 if (product?.VariantInfo?.VariantInfo != null) 762 { 763 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 764 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 765 } 766 if (priceMin != priceMax) 767 { 768 price = priceMin + " - " + priceMax; 769 } 770 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div> 771 } 772 } 773 </div> 774 } 775 776 <div class="icon-compare mt-2 d-none d-sm-flex justify-content-center align-items-center fs-7" 777 data-number="@HttpUtility.HtmlAttributeEncode(product.Number)" 778 data-image="@HttpUtility.HtmlAttributeEncode(imagePath)" 779 data-name="@HttpUtility.HtmlAttributeEncode(product.Name)" 780 data-link="@HttpUtility.HtmlAttributeEncode(link)" 781 onclick="toggleCompareProduct(event, this)"> 782 <span class="square-icon js-square-icon d-flex" data-product-number="@HttpUtility.HtmlAttributeEncode(product.Number)"></span> 783 <span>@Translate("Sammenlign")</span> 784 </div> 785 786 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches") 787 { 788 var optionCount = product.VariantInfo.VariantInfo.Count(); 789 var showMaxVariants = 5; 790 791 <div class="d-flex flex-row gap-1 align-items-center"> 792 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants)) 793 { 794 <span class="colorbox colorbox-sm rounded-circle border me-1" style="background-color: @variant.OptionColor"></span> 795 } 796 @if (optionCount > showMaxVariants) 797 { 798 int left = optionCount - showMaxVariants; 799 <span class="ms-2">+@left</span> 800 } 801 </div> 802 } 803 804 <div class="mt-3"> 805 <form method="post" action="@cartUrl"> 806 <input type="hidden" name="redirect" value="false" /> 807 <input type="hidden" name="ProductId" value="@product.Id" /> 808 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)" /> 809 <input type="hidden" name="ProductVariantName" value="@HtmlEncoder.HtmlEncode(product.VariantName)" /> 810 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code" /> 811 <input type="hidden" name="ProductPrice" value="@(showPricesWithVat == "false" && !neverShowVat ? product.Price.PriceWithoutVat.ToString("0.00", CultureInfo.InvariantCulture) : product.Price.Price.ToString("0.00", CultureInfo.InvariantCulture))" /> 812 <input type="hidden" name="ProductReferer" value="component_ProductListGridView_Custom"> 813 <input type="hidden" name="cartcmd" value="add" /> 814 <input type="hidden" name="quantity" value="1" /> 815 <input type="hidden" class="js-product-id" value="@product.Id" /> 816 817 @if (!string.IsNullOrEmpty(product.VariantId)) 818 { 819 <input type="hidden" name="VariantId" value="@product.VariantId" /> 820 } 821 822 @*//CUSTOM*@ 823 <div class="d-flex flex-column gap-2"> 824 @if (enableContact) 825 { 826 var enableContactlink = Pageview.AreaSettings.GetItem("CustomSettings").GetLink("Custom_EnableContactLink"); 827 if (enableContactlink != null) 828 { 829 <a href="@(enableContactlink.Url)" class="btn btn-tertiary flex-fill" title="@HttpUtility.HtmlAttributeEncode(Translate("Contact us about price and delivery >>"))"> 830 @Translate("Contact us about price and delivery >>") 831 </a> 832 } 833 } 834 835 @if (enableBookDemo) 836 { 837 var enableBookDemolink = Pageview.AreaSettings.GetItem("CustomSettings").GetLink("Custom_EnableBookDemoLink"); 838 if (enableBookDemolink != null) 839 { 840 <a href="@(enableBookDemolink.Url)" class="btn btn-tertiary flex-fill" title="@HttpUtility.HtmlAttributeEncode(Translate("Contact us about book demo >>"))"> 841 @Translate("Contact us about book demo >>") 842 </a> 843 } 844 } 845 846 @if (!disablePurchase) 847 { 848 if (!specs.GetByKey("Preorder").Value.IsYes()) 849 { 850 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button" title="@HttpUtility.HtmlAttributeEncode(Translate("Add to cart"))" id="AddToCartButton@(product.Id)"> 851 @Translate("Add to cart") 852 </button> 853 } 854 else 855 { 856 <button type="button" class="btn btn-secondary flex-fill" onclick="Form(this).addData('@HttpUtility.HtmlEncode(product.Name)','@product.Id')" data-bs-toggle="modal" data-bs-target="#preorderModal"> 857 @Translate("Preorder") 858 </button> 859 } 860 } 861 </div> 862 @*//CUSTOM*@ 863 </form> 864 </div> 865 866 </div> 867 868 </div> 869 </article> 870 } 871 </div> 872 873 <div class="my-3" id="LoadMoreButton"> 874 <div class="text-center d-flex flex-column gap-3"> 875 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div> 876 @if (productList.PageCount != 1) 877 { 878 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? ""; 879 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection; 880 881 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : ""; 882 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : ""; 883 884 <form method="get" action="@url" data-response-target-element="content" class="w-100"> 885 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 886 { 887 foreach (FacetViewModel facetItem in facetGroup.Facets) 888 { 889 foreach (FacetOptionViewModel facetOption in facetItem.Options) 890 { 891 if (facetOption.Selected) 892 { 893 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]"> 894 } 895 } 896 } 897 } 898 899 <input type="hidden" name="PageSize" value="@pageSize"> 900 <input type="hidden" name="SortBy" value="@sortBySelection"> 901 <input type="hidden" name="RequestType" value="UpdateList"> 902 903 @if (!string.IsNullOrEmpty(searchQuery)) 904 { 905 <input type="hidden" name="q" value="@HttpUtility.HtmlAttributeEncode(searchQuery)"> 906 <input type="hidden" name="SearchLayout" value="@searchLayout"> 907 } 908 909 @{ 910 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection; 911 912 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 913 { 914 foreach (FacetViewModel facetItem in facetGroup.Facets) 915 { 916 foreach (FacetOptionViewModel facetOption in facetItem.Options) 917 { 918 if (facetOption.Selected) 919 { 920 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]"; 921 } 922 } 923 } 924 } 925 926 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : ""; 927 } 928 929 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a> 930 </form> 931 } 932 </div> 933 </div> 934 } 935 else 936 { 937 if (!Pageview.IsVisualEditorMode) 938 { 939 <div class="alert alert-dark m-0"> 940 @Translate("We did not find anything matching your search result") 941 </div> 942 } 943 else 944 { 945 <div class="alert alert-dark m-0" role="alert"> 946 <span>@Translate("Product list: The list will be shown here, if any")</span> 947 </div> 948 } 949 } 950 } 951 952 @helper RenderField(FieldValueViewModel field) 953 { 954 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 955 956 if (fieldValue != "") 957 { 958 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 959 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 960 961 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 962 { 963 fieldValue = ""; 964 965 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 966 { 967 fieldValue = option.Name; 968 } 969 } 970 971 bool isColor = false; 972 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) 973 { 974 isColor = true; 975 } 976 977 if (!string.IsNullOrEmpty(fieldValue)) 978 { 979 if (!isColor) 980 { 981 <li>@(field.Name): @fieldValue</li> 982 } 983 else 984 { 985 <li class="position-relative"> 986 <span class="colorbox-sm" style="background-color: @fieldValue"></span> 987 </li> 988 } 989 } 990 } 991 }
Tamron objektiver
Tamron er en velrenommeret producent af tredjepartsobjektiver, der tilbyder et bredt udvalg af objektiver til forskellige kamerafatninger, herunder Canon EF, Nikon F, Sony E-mount og Fuji XF. Her er nogle af de vigtigste objektivserier fra Tamron:
- Tamron SP (Super Performance)-serien: Tamron SP-objektiver er flagskibsobjektiverne fra Tamron og er kendt for deres høje optiske ydeevne og byggekvalitet. Disse objektiver tilbyder en bred vifte af brændvidder og blændeåbninger og er velegnede til forskellige genrer som portrætter, landskaber og makrofotografering.
- Tamron Di (Digitally Integrated Design)-serien: Tamron Di-objektiver er designet specielt til digitale spejlreflekskameraer og tilbyder avancerede optiske teknologier til at opnå høj billedkvalitet og kontrast. Disse objektiver er alsidige og dækker forskellige brændvidder og blændeåbninger.
- Tamron VC (Vibration Compensation)-serien: Tamron VC-objektiver er udstyret med Tamrons billedstabiliseringsteknologi, der hjælper med at reducere kamerarystelser og giver skarpere billeder ved håndholdt fotografering under svag belysning eller med længere brændvidder.
- Tamron SP (Super Performance) Di II-serien: Tamron SP Di II-objektiver er specifikt designet til APS-C-sensor kameraer og tilbyder en bred vifte af brændvidder og blændeåbninger. Disse objektiver er populære inden for landskabs-, portræt- og rejsefotografering.
Tamron er kendt for at tilbyde objektiver til en konkurrencedygtig pris uden at gå på kompromis med optisk kvalitet. Deres objektiver dækker et bredt spektrum af brændvidder og er populære blandt både professionelle og entusiastfotografer. Når du vælger et Tamron-objektiv, er det vigtigt at sikre kompatibilitet med din specifikke kamerafatning.