Liquid, JavaScript, Themes
Hallo Forum,
ich möchte auf der Productview eine Customization Box (z. B. Gravierungen) hinzufügen. Erster Gedanke war mit Metafields zu arbeiten, jedoch erschließt sich mir nicht, wie ich aus der Productview/Storefront aus Metafields aktualisieren kann. Wenn ich das nun richtig verstanden habe, wird das nur mittels Storefront API/ GraphQL mutations möglich sein, korrekt ? Wobei der Storefront Approach für das Vorhaben eher Overkill ist und wohl auch mit AJAX zu lösen ist, oder ?
Ich stehe gerade auf der Leitung, wäre um jeden "Schubser" dankbar 😛
Gelöst! Zur Lösung
Erfolg.
Hey @Gabe
Die App nimmt immer mehr Form an. Aktuell im Codereview. Ich teile gerne meinen Liquid Code mit euch, schaut mal hier:
3 Inputs (Image Upload, Heading, Description)
Image restrictions: max. 800x800, .jpeg-only, max. 2MB
Heading max. 50 digits
Description max. 5 Lines
{% comment %}
@feature Customization Section/Block for PDP
@theme Dawn 10.0
{% endcomment %}
<div>
<div class="product-form__input">
<label class="form__label">Image Upload</label>
<input
id="image"
type="file"
form="{{ 'product-form-' | append: section.id }}"
name="properties[Image Upload]"
accept=".jpg"
max-width="800"
max-height="800"
max-size="2"
required>
<script>
document.addEventListener("DOMContentLoaded", ()=>{document.querySelector("form[novalidate]").removeAttribute("novalidate")})
</script>
</div>
<style>.custom.form__label{margin-bottom: 0.6rem}.field.custom{margin-top:0}.custom .field__input{padding-top:0.8rem}</style>
<label class="form__label custom" for="heading">Heading</label>
<div class="field custom">
<input
class="field__input"
form="{{ 'product-form-' | append: section.id }}"
type="text"
id="heading"
name="properties[Heading]"
maxlength="50"
required
>
<script>
document.addEventListener("DOMContentLoaded", ()=>{document.querySelector("form[novalidate]").removeAttribute("novalidate")})
document.querySelector('#heading').addEventListener('input',(e)=>{
if(!document.querySelector('label[for="heading"] span')){
const span = document.createElement("span");
span.style.marginLeft = '10px'
document.querySelector('label[for="heading"]').appendChild(span)
}
document.querySelector('label[for="heading"] span').style.display = 'inline'
document.querySelector('label[for="heading"] span').textContent = e.target.value.length + ' | ' + 50
if(e.target.value.length==0){
document.querySelector('label[for="heading"] span').style.display = 'none'
}})
</script>
</div>
<style>.custom.form__label{margin-bottom: 0.6rem}.field.custom{margin-top:0}.custom .field__input{padding-top:0.8rem}</style>
<label class="form__label custom" for="description">Description</label>
<div class="field custom">
<textarea
class="text-area field__input"
id="description"
form="{{ 'product-form-' | append: section.id }}"
name="properties[Description]">
</textarea>
<script>
document.getElementById('description').addEventListener('input', function() {
var lines = this.value.split('\n');
if (lines.length > 5) {
alert('Maximum 5 lines allowed.');
this.value = lines.slice(0, 5).join('\n');
}
});
</script>
</div>
</div>
<script>
{% comment %} Logic for Image Requirements {% endcomment %}
document.getElementById('image').addEventListener('change', function() {
var fileInput = this;
var maxWidth = fileInput.getAttribute('max-width');
var maxHeight = fileInput.getAttribute('max-height');
var maxSize = fileInput.getAttribute('max-size');
var file = fileInput.files[0];
if (file.type !== 'image/jpeg') {
alert('Please select a valid JPG image.');
fileInput.value = ''; // clear Input
return;
}
var img = new Image();
img.onload = function() {
if (img.width > maxWidth || img.height > maxHeight) {
alert('Image dimensions must be at most 800x800 pixels.');
fileInput.value = ''; // clear Input
}
};
img.src=URL.createObjectURL(file);
if (file.size > maxSize * 1024 * 1024) {
alert('File size must be at most 2MB.');
fileInput.value = ''; // clear Input
}
});
</script>
{% schema %}
{
"name": "PDP Customization",
"target": "section"
}
{% endschema %}
Bei Fragen, gerne melden !
cheers,
sam
Hey @MosDev__
Ich werde mal hier reinspringen denn es nicht ganz klar was du mit "Productview" genau meinst - ein "Quickview" auf der Collection Page, oder die PDP?
Metafields sind zwar flexibel und ermöglichen es, zusätzliche Informationen zu Produkten zu speichern, wie z.B. Gravierungstexte. Aber da machst du dir 'ne Heidenarbeit denn das ganze gibt's ja meistens im Theme oder mit einer Product Options/Metafields App meistens out-of-the-box.
Das Aktualisieren von Metafields aus der Storefront heraus ist auch nicht direkt möglich so wie ich das verstehe, da normalerweise werden Metafields im Shopify Admin/Admin API aktualisiert. Es gibt auch Apps wie Metafields Manager oder Metafields Guru, die es ermöglichen, Metafields zu verwalten, ohne direkt Code schreiben zu müssen.
Die Verwendung der Storefront API mit GraphQL-Mutationen könnte gehen, aber wie du schon sagst, wäre etwas Overkill. Somit könnte man Metafields über die Storefront API abrufen, was aber erfordert, dass die Metafields zuerst der API zugänglich gemacht werden. Dann kannst du über GraphQL Abfragen diese Informationen abrufen.
AJAX in Kombination mit den Shopify Ajax APIs oder mit einem Shopify App Proxy zu verwenden könnte auch gehen da mit AJAX kann man asynchron Daten an den Server senden, ohne die Seite refreshen zu müssen. Eben eine serverseitige Logik verwenden, um die Metafields zu aktualisieren.
Um Metafields direkt von der Produktseite aus zu aktualisieren, um asynchron Anfragen an deinen Server zu senden - es ist zu lesen, dass das Anhängen von 'json' oder 'js' an eine Produkt-URL Daten zurückgeben kann, ohne dass eine Authentifizierung erforderlich ist. Damit kann man ggf. Informationen abrufen, die dann zur Aktualisierung von Metafields genutzt werden könnten.
Ein einfacher Ablauf könnte wie folgt aussehen:
Hoffe das bringt dich in die richtige Richtung. Gerne kann ich bei weiteren Fragen Experten empfehlen! 😉
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Hey @Gabe
danke für deine ausführliche Antwort, ja ich meinte die PDP. Ich arbeite mal deine Vorschlaege heute durch und wuerde mich bei Rueckfragen melden. Es wird vermutlich auf die Overill Variante rauslaufen, da ich die Values der Metafields mit in den Cart / Checkout uebernehmen soll. On the long run sollte auch ein Shopify Admin Panel dafuer gebaut werden, um die Bestellungen mit customizations anzuzeigen. Aber bis dahin reicht erst einmal der Part auf der PDP
Cheers,
sam
Ok, hört sich gut an Sam! @MosDev__
Einen habe ich noch haha...das habe ich etwas weiter recherchiert und vielleicht hilft das auch:
Um Metafields in Shopify für die Storefront API zugänglich zu machen und dann über GraphQL abfragen zu können, hier ein paar weitere Schritte die ich aus unserer Developer Doku entnommen habe (vielleicht kannst du gebrauchen 😉 ) :
Metafield-Definition erstellen:
Einstellungen
> Benutzerdefinierte Daten
.Metafields
die Kategorie aus, für die du ein Metafield erstellen möchtest (z.B. Produkte, Bestellungen).Definition hinzufügen
.Zugriffsoptionen
aktiviere die Option Storefront
. Dies ermöglicht es, dass der Inhalt von deinem Online-Shop abgerufen werden kann.Werte zu Metafields hinzufügen:
Metafields über die Storefront API abrufen:
Hier ist ein einfaches Beispiel für eine GraphQL-Abfrage, die ein Metafield eines Produkts abruft:
{
productByHandle(handle: "my-product-handle") {
id
title
metafield(namespace: "mein_namespace", key: "mein_schluessel") {
value
}
}
}
Da ersetzt du "my-product-handle"
mit dem Handle deines Produkts und "mein_namespace"
sowie "mein_schluessel"
mit dem entsprechenden Namespace und Schlüssel deines Metafields.
Alles weitere dazu hier. Und halte mich auf dem Laufenden wie das geht! 😉
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Hey @Gabe
nochmal vielen Dank fuer dein Kommentar, das hat mich in die richtige Richtung gebracht.
Aktueller Stand ist folgender:
Jetzt bin ich gerade dran, zu schauen wie weit ich hier mit der Storefront API komme, es scheint aber so, als kann die Storefront API nur lesen und nicht schreiben, also mutations werden damit nicht gehen.
Wie geht man da generell mit sensiblen Daten wie z. B. Admin API Access Token um ? Ich koennte sie in eine .env ablegen, bin allerdings unsicher ob das best practice ist ?
nächste frage ist wie ich die mutation in den quellcode bekomme und ausfuehren lasse. Ich denke die Antwort werde ich im Laufe des Tages finden und mich erneut melden.
Greets,
sam
Hey Sam! @MosDev__
Das Arbeiten mit sensiblen Daten wie Admin API Access Tokens in Shopify und das Durchführen von Mutationen erfordert einige Überlegungen bzgl. der Sicherheit und der Implementierung. Hier sind allgemeine Best Practices und Schritte, die ich dazu gefunden habe:
Umgang mit Sensiblen Daten (z.B. Admin API Access Tokens)
.env
-Datei wird nicht in deinem Quellcode-Repository gespeichert und hilft dabei, sensible Informationen sicher zu halten. Stelle sicher, dass die .env
-Datei in deiner .gitignore
-Datei aufgelistet ist, um ein versehentliches Hochladen auf öffentliche Repositories zu vermeiden.Mutationen in den Quellcode Integrieren und Ausführen
Hoffe das hilft dir weiter, Sam! 😉
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Hi @Gabe,
Frohes Neues Jahr erst einmal !
ich bin weiter dran an der App und wollte an dieser Stelle kurzes Brainstorming/Rubberducking betreiben.
Die App soll ja Kunden die Möglichkeit bieten, eine Customization über Inputs auf der ProductDetailPage einzugeben und am Ende sollen diese Daten mit in den Cart/Checkout in die Order wandern.
Die aktuelle Idee wäre folgende:
Wichtig an der Stelle ist es zu erwähnen, dass ich mit der Mutation das Metafield eines Produkt aktualisiere. Man könnte die Mutation so schreiben, dass jedes mal ein neues Metafield erzeugt wird - bin mir aber unsicher ob das Best Practice ist.
Meine weitere Überlegung ist folgende:
Das Problem ist ja, wenn ich 2 User gleichzeitig an dem selben Produkt eine Customization machen, gewinnt ja nur die Mutation welche zuletzt gemacht wurde, deswegen muss die Customization für jeden User separat abgelegt werden und wieder mit in den Cart genommen werden soll.
Macht das Sinn oder siehst du eine andere Möglichkeit ?
Greets,
sam
Hey SaM! @MosDev__
Da bin ich wieder! Wie war dein New Year? Hoffe gut... 😉
Deine App, die es Kunden ermöglicht, Produkte auf der Produkt-Detailseite (PDP) zu individualisieren, wird eine tolle Sache! Hast du schon ein Name oder Branding für die App? 2024 wird ein spannendes Jahr!
Die Sorge hinsichtlich der Handhabung gleichzeitiger Anpassungen durch verschiedene Nutzer ist auch berechtigt. Deine Idee, einen Bild-Upload, Text und Bemerkungen als Inputs auf der PDP zu integrieren und dann per AJAX an den Node-Server zu senden, könnte gehen und es deinen User ermöglichen eine seamless UX zu geniessen ohne die Notwendigkeit, die Seite neu laden zu müssen, was ja keine gute UX ist, aus versch. Gründen.
Das Aktualisieren der Metafields eines Produkts erlaubt das Speichern zusätzlicher Informationen. Ob du für jede Customization ein neues Metafield erstellen solltest oder nicht, hängt aber von deinem genauen Use-Case ab. Wenn beispielsweise die Customizations nur temporär sind und für den aktuellen Einkauf relevant, könnte es besser sein, sie separat und nicht direkt als Metafield des Produkts zu speichern.
Dann die Customization-Daten in einer SQLite-Datenbank mit der Cart-Session-ID zu verknüpfen, scheint ein guter Weg zu sein, um die Daten für jeden Benutzer individuell zu halten. So vermeidest du beispielsweise das Problem, dass die letzte Mutation die vorherigen überschreibt.
Der schwierigste Teil scheint die Übernahme der Metafields in den Warenkorb und Checkout zu sein. Eine Möglichkeit könnte sein, die Customization-Daten in der Datenbank zu speichern und dann beim Checkout eine Verbindung zwischen diesen Daten und der jeweiligen Bestellung herzustellen. Du könntest auch überlegen, ob es sinnvoller ist, die Customization-Informationen als Teil des Line-Item-Objekts im Warenkorb zu speichern, anstatt sie als Metafields an Produkten anzuhängen. Dies könnte es einfacher machen, die Individualisierungen mit den Bestellungen zu verknüpfen.
Ab der API-Version 2023-04 wurden Metafelder für die Warenkorbrssource eingeführt. Diese Erweiterung bietet neue Möglichkeiten für die Speicherung von benutzerdefinierten Daten auf Warenkorbebene (siehe in der Dev Doku die neue CartTransform Funktion). Allerdings gibt es noch Unsicherheiten darüber, wie diese Metafelder in Shopify-Funktionsinputs integriert werden können.
Es gibt auch eine Diskussionen über die Möglichkeit, Metafelder, die im Warenkorb gesetzt wurden, automatisch in Bestellungen zu übernehmen, wenn diese aufgegeben werden. Dies könnte angeblich durch die Verwendung von Webhooks oder durch direktes Speichern der Metafeldwerte in der Bestellung selbst erreicht werden.
Es könnte also sinnvoll sein, die Metafelder auf Warenkorbebene zu nutzen, um die Anpassungen zu speichern und sicherzustellen, dass sie mit der Bestellung verknüpft werden.
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Hey @Gabe,
Danke für deine ausführliche Antwort 😛 - ich komme dem Ziel mit der App immer näher. Ich komme auf deine Vorschläge aus deiner letzten Antwort im Laufe des nachmittags noch zurück, einige gute Ansätze die ich mitnehmen kann.
Gehen wir einen Schritt zurück, denn es hakt an einem wichtigen Punkt auf meiner Seite - ich bin mir nicht ganz klar darüber, wie ich die Daten aus den Inputs von der Storefront an meine Remix App im Backend schicken kann. Wir hatten weiter oben über AJAX gesprochen, habe mir dazu einiges an Content durchgelesen und angeschaut, habe jedoch Probleme beim Umsetzen. Hast du vielleicht ein einfaches Beispiel für mich im Sinn ? Ich habe mein Formular (extensions/customization/form.liquid) und mein remix backend (app/server.jsx).
Für meine form.liquid hatte ich an so etwas gedacht:
<form method="post" action="/cart/update" id="dataForm" enctype="multipart/form-data">
<!-- Texteingabe -->
<label for="customer-input">Kundeninput:</label>
<input type="text" id="customer-input" name="properties[customText]" required>
<!-- Bildupload -->
<label for="customer-image">Bild hochladen:</label>
<input type="file" id="customer-image" name="properties[customImg]" accept="image/*" required>
<!-- Multi-Line Texteingabe -->
<label for="customer-comments">Bemerkung:</label>
<input type="text" id="customer-comments" name="properties[customComments]" required>
<!-- Button zum Hinzufügen -->
<button type="button" onclick="dataCollect()">Send</button>
</form>
<script>
function dataCollect() {
// Informationen aus den Input-Feldern sammeln
var customerInput = document.getElementById('customer-input').value;
var customerImage = document.getElementById('customer-image').value;
var customerComments = document.getElementById('customer-comments').value;
// Daten in einem JSON-Objekt zusammenfassen
var daten = {
textInput: customerInput,
imageInput: customerImage,
commentInput: customerComments
};
// Daten an den Server senden (hier wird die Funktion zum Senden angenommen)
sendDataToBackend(daten);
}
function sendDataToBackend(daten) {
fetch('/dein-endpunkt', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(daten)
})
.then(response => response.json())
.then(data => {
console.log('Antwort vom Server:', data);
})
.catch(error => {
console.error('Fehler beim Senden der Daten:', error);
});
}
</script>
Bin mir nicht ganz sicher, wie mein fetch Endpoint in der setDataToBackend() ausschauen muss. Hast du eine Idee ?
cheers,
sam
Hey Sam! @MosDev__
Deine Idee, AJAX für das Sammeln und Senden von Benutzer-Inputs von der Product Detail Page (PDP) zum Server zu nutzen, ist definitiv ein guter Ansatz und könnte eine reibungslose UX ermöglichen, da die Seite dann nicht neu geladen werden muss. Hier ein paar weitere Punkte die meine Recherche unserer Entwickler Doku und der verschiedenen API Forums wie MDN Web Docs und W3Schools ergeben hat (alles aber ohne gewähr und musst du selber testen) :
fetch()
fetch()
-Aufruf in deiner sendDataToBackend
-Funktion scheint korrekt zu sein. Du sendest die Daten als JSON an deinen Server.'/dein-endpunkt'
), sollte die URL sein, die von deinem Node-Server oder deinem Remix Backend verarbeitet wird. Diese URL ist der API-Endpunkt, den du auf deinem Server definieren musst, um die eingehenden Daten zu empfangen und zu verarbeiten.Um Daten von einer HTML-Form an einen Node.js-Server zu senden, gibt es verschiedene Ansätze. Die allgemeine Methode besteht darin, die Daten mit AJAX zu erfassen und sie an einen Server-Endpunkt zu senden, der für ihre Verarbeitung zuständig ist.
Erstellen einer HTML-Form: Deine HTML-Form kann verschiedene Eingabefelder enthalten. Ein Beispiel für eine einfache Form mit einem Textfeld könnte so aussehen:
<form action="/team_name_url/" method="post">
<label for="team_name">Enter name: </label>
<input id="team_name" type="text" name="name_field" value="Default name for team." />
<input type="submit" value="OK" />
</form>
$('#formId').on('submit', function(e) {
e.preventDefault();
var data = $(this).serialize();
$.ajax({
type: 'POST',
url: '/dein-endpunkt',
data: data,
success: function(response) {
// Handle success
},
error: function(error) {
// Handle error
}
});
});
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.post('/dein-endpunkt', (req, res) => {
// Verarbeite die Daten
});
app.listen(3000, () => {
console.log('Server läuft auf Port 3000');
});
Stelle sicher, dass die Daten auf dem Server validiert und bereinigt werden, um Sicherheitsrisiken wie SQL-Injection oder Cross-Site Scripting zu vermeiden.
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Hey @Gabe,
Danke für deine Antwort - der Input war sehr hilfreich !
Es gibt gute Neuigkeiten, ich hab das Feature zu Ende entwickelt bekommen - allerdings mit einem völlig anderen Ansatz als den, welchen wir die letzten 2 Wochen besprochen hatten. Mit GraphQL/mutations etc. wäre das ebenfalls alles möglich gewesen, jedoch nach langem Chat mit meinem Kollegen ein absoluter Overkill bzw. way-to-complicated.
Die Antwort sind simple Custom Field Input, welche an die Product Form angehängt werden und vom Cart über den Checkout bis in die Order Details mitgegeben werden. Die notwendigen Input Felder konnte ich mir hier generieren lassen -> https://amineammari.com/field-generator-for-shopify-product-pages/
Vielen Dank für deine Unterstützung mit dieser Frage ! Hier wird einem geholfen 😛
cheers,
sam
Wow, das hört sich viel besser an! Poste hier einen Link zu deiner App sobald sie proof-of-concept ist und wir können auch etwas User-Testing durchführen!! 😉
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Erfolg.
Hey @Gabe
Die App nimmt immer mehr Form an. Aktuell im Codereview. Ich teile gerne meinen Liquid Code mit euch, schaut mal hier:
3 Inputs (Image Upload, Heading, Description)
Image restrictions: max. 800x800, .jpeg-only, max. 2MB
Heading max. 50 digits
Description max. 5 Lines
{% comment %}
@feature Customization Section/Block for PDP
@theme Dawn 10.0
{% endcomment %}
<div>
<div class="product-form__input">
<label class="form__label">Image Upload</label>
<input
id="image"
type="file"
form="{{ 'product-form-' | append: section.id }}"
name="properties[Image Upload]"
accept=".jpg"
max-width="800"
max-height="800"
max-size="2"
required>
<script>
document.addEventListener("DOMContentLoaded", ()=>{document.querySelector("form[novalidate]").removeAttribute("novalidate")})
</script>
</div>
<style>.custom.form__label{margin-bottom: 0.6rem}.field.custom{margin-top:0}.custom .field__input{padding-top:0.8rem}</style>
<label class="form__label custom" for="heading">Heading</label>
<div class="field custom">
<input
class="field__input"
form="{{ 'product-form-' | append: section.id }}"
type="text"
id="heading"
name="properties[Heading]"
maxlength="50"
required
>
<script>
document.addEventListener("DOMContentLoaded", ()=>{document.querySelector("form[novalidate]").removeAttribute("novalidate")})
document.querySelector('#heading').addEventListener('input',(e)=>{
if(!document.querySelector('label[for="heading"] span')){
const span = document.createElement("span");
span.style.marginLeft = '10px'
document.querySelector('label[for="heading"]').appendChild(span)
}
document.querySelector('label[for="heading"] span').style.display = 'inline'
document.querySelector('label[for="heading"] span').textContent = e.target.value.length + ' | ' + 50
if(e.target.value.length==0){
document.querySelector('label[for="heading"] span').style.display = 'none'
}})
</script>
</div>
<style>.custom.form__label{margin-bottom: 0.6rem}.field.custom{margin-top:0}.custom .field__input{padding-top:0.8rem}</style>
<label class="form__label custom" for="description">Description</label>
<div class="field custom">
<textarea
class="text-area field__input"
id="description"
form="{{ 'product-form-' | append: section.id }}"
name="properties[Description]">
</textarea>
<script>
document.getElementById('description').addEventListener('input', function() {
var lines = this.value.split('\n');
if (lines.length > 5) {
alert('Maximum 5 lines allowed.');
this.value = lines.slice(0, 5).join('\n');
}
});
</script>
</div>
</div>
<script>
{% comment %} Logic for Image Requirements {% endcomment %}
document.getElementById('image').addEventListener('change', function() {
var fileInput = this;
var maxWidth = fileInput.getAttribute('max-width');
var maxHeight = fileInput.getAttribute('max-height');
var maxSize = fileInput.getAttribute('max-size');
var file = fileInput.files[0];
if (file.type !== 'image/jpeg') {
alert('Please select a valid JPG image.');
fileInput.value = ''; // clear Input
return;
}
var img = new Image();
img.onload = function() {
if (img.width > maxWidth || img.height > maxHeight) {
alert('Image dimensions must be at most 800x800 pixels.');
fileInput.value = ''; // clear Input
}
};
img.src=URL.createObjectURL(file);
if (file.size > maxSize * 1024 * 1024) {
alert('File size must be at most 2MB.');
fileInput.value = ''; // clear Input
}
});
</script>
{% schema %}
{
"name": "PDP Customization",
"target": "section"
}
{% endschema %}
Bei Fragen, gerne melden !
cheers,
sam
Wow sieht super aus! Gute arbeit Sam! 😉
Gabe | Social Care @ Shopify
- War meine Antwort hilfreich? Klicke Like um es mich wissen zu lassen!
- Wurde deine Frage beantwortet? Markiere es als Akzeptierte Lösung
- Um mehr zu erfahren, besuche das Shopify Help Center oder den Shopify Blog
Teil 2 - Wie die Prinzipien des UX-Designs dir dabei helfen können einen großartigen Shop ...
By Kai Sep 16, 2024Teil 1 - Wie die Prinzipien des UX-Designs dir dabei helfen können einen großartigen Shop ...
By Kai Sep 9, 2024Anpassungen des benutzerdefinierten Codes an Shopify-Themes (CSS) leicht gemachtIn diesem...
By Gabe Aug 28, 2024