# CMS Element | Mehrere media uploads -\> Wie upload listener und media modal?

**URL:** https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149
**Category:** Programmierung
**Created:** [8. September 2022 um 20:38 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149 "2022-09-08T20:38:24Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![AlexBS](https://avatars.discourse-cdn.com/v4/letter/a/ed655f/32.png) [@AlexBS](https://forum.shopware.com/u/AlexBS)
#### Post date: [8. September 2022 um 20:38 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149/1 "2022-09-08T20:38:24Z")

</div>

Hallo,

ich habe ein Cms Element und bisher nur ein Bild mit Upload benötigt. Habe mich am image Cms Element von SW orientiert und dort alle JS Funktionen kopiert, genauso im Twig Template ein mapping-field, ein media-upload-vs, einen upload-listener und ein media-modal-v2. Alles funktioniert wunderbar.

Nun benötige ich noch ein zweites Bild inkl. Upload Funktion. Brauche ich jetzt einen zweiten upload-listener und ein zweites media-modal-v2? Die Funktionen, die mit diesen components verknüpft sind, beziehen sich ja explizit nur auf ein bestimmtes media Feld in der config.

z.B. im Twig template

```auto
<sw-upload-listener
    :upload-tag="uploadTag"
    auto-upload
    @media-upload-finish="onImageUpload"
 />

```

und dann in der JS Datei:

```auto
async onImageUpload({ targetId }) {
    const mediaEntity = await this.mediaRepository.get(targetId);
    this.element.config.media.value = mediaEntity.id;
    this.updateElementData(mediaEntity);
    this.$emit('element-update', this.element);
 }

```

Ich würde dann ja auch alle anderen Funktionen, zB previewSource, onImageRemove etc. kopieren müssen. Ich habe noch kein Cms-Element gefunden, an dem ich mich diesbezüglich orientieren könnte.

Wie setzt ihr so etwas um?

Lg Alex

---

<div class="post-metadata">

### Author: ![AlexBS](https://avatars.discourse-cdn.com/v4/letter/a/ed655f/32.png) [@AlexBS](https://forum.shopware.com/u/AlexBS)
#### Post date: [9. September 2022 um 16:58 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149/2 "2022-09-09T16:58:00Z")

</div>

Niemand bisher das gleiche Szenario gehabt? Kann doch nicht sein ☹

---

<div class="post-metadata">

### Author: ![AlexBS](https://avatars.discourse-cdn.com/v4/letter/a/ed655f/32.png) [@AlexBS](https://forum.shopware.com/u/AlexBS)
#### Post date: [17. September 2022 um 12:07 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149/3 "2022-09-17T12:07:14Z")

</div>

> <https://github.com/shopware/platform/issues/2427>
>
> \### Please describe the feature you would like to see implemented.
> 
> When creatin…g CMS elements it's currently very cumbersome to implement a media upload field which uses \`sw-media-upload-v2\`.  
> 
> I know of \`sw-media-field\` but I don't really like it, \`sw-media-upload-v2\` is much better. I couldn't find a better component for media upload for now.
> 
> Until now I copied the logic from https://github.com/shopware/platform/blob/26f5f18d499536139555029a40f29883a011280d/src/Administration/Resources/app/administration/src/module/sw-cms/elements/image/config/index.js but this still needs a lot of code in my own component. Also this logic would all have to be duplicated for multiple upload fields.
> 
> Now I had the case where I had two images in my element config and wasn't happy with code duplication so I wrote a mixin for it which I can use like in the code below. This is still a lot of code and I would be happy to be able to just add a \`\<sw-media-upload-v3 v-model="media" @media-upload-update="emitElementUpdate"\>\` (or similiar) which abstracts all that logic away.
> 
> \<details\>
> \<summary\>index.js\</summary\>
> 
> \`\`\`js
> import template from './sw-cms-el-config-link-tile.html.twig';
> import './sw-cms-el-config-link-tile.scss';
> 
> const { Component, Mixin } = Shopware;
> 
> Component.register('sw-cms-el-config-link-tile', {
> template,
> 
> mixins: \[
> Mixin.getByName('cms-element'),
> Mixin.getByName('cms-media-field'),
> \],
> 
> data() {
> return {
> mediaFields: {}
> }
> },
> 
> watch: {
> cmsPageState: {
> deep: true,
> handler() {
> this.$forceUpdate();
> },
> },
> },
> 
> created() {
> this.createdComponent();
> },
> 
> methods: {
> createdComponent() {
> this.initElementConfig('link-tile');
> this.initElementData('link-tile');
> 
> this.mediaFields = {
> backgroundImage: this.createMediaField('backgroundImage'),
> iconImage: this.createMediaField('iconImage'),
> }
> },
> },
> });
> \`\`\`
> 
> \</details\>
> 
> \<details\>
> \<summary\>sw-cms-el-config-link-tile.html.twig\</summary\>
> 
> \`\`\`twig
> {% block sw\_cms\_element\_link\_tile\_config %}
> \<div class="sw-cms-el-config-link-tile"\>
> {% block sw\_cms\_element\_link\_tile\_config\_media\_upload %}
> \<template v-for="mediaField in mediaFields"\>
> \<sw-cms-mapping-field
> v-model="element.config\[mediaField.name\]"
> :label="$tc('vc-scholl.sw-cms.elements.link-tile.config.label.' + mediaField.name)"
> value-types="entity"
> entity="media"
> \>
> \<sw-upload-listener
> :key="mediaField.name + '-upload-listener'"
> :upload-tag="mediaField.getUploadTag()"
> auto-upload
> @media-upload-finish="mediaField.onImageUpload"
> /\>
> \<sw-media-upload-v2
> :key="mediaField.name + '-media-upload'"
> variant="regular"
> :upload-tag="mediaField.getUploadTag()"
> :source="mediaField.getMedia()"
> :allow-multi-select="false"
> :default-folder="cmsPageState.pageEntityName"
> :caption="$tc('sw-cms.elements.general.config.caption.mediaUpload')"
> @media-upload-sidebar-open="mediaField.onOpenMediaModal"
> @media-upload-remove-image="mediaField.onImageRemove"
> /\>
> 
> \<div
> slot="preview"
> slot-scope="{ demoValue }"
> class="sw-cms-el-config-link-tile\_\_mapping-preview"
> \>
> \<img
> v-if="demoValue.url"
> :src="demoValue.url"
> alt=""
> \>
> \<sw-alert
> v-else
> class="sw-cms-el-config-link-tile\_\_preview-info"
> variant="info"
> \>
> {{ $tc('sw-cms.detail.label.mappingEmptyPreview') }}
> \</sw-alert\>
> \</div\>
> \</sw-cms-mapping-field\>
> \</template\>
> {% endblock %}
> 
> {% block sw\_cms\_element\_link\_tile\_config\_media\_modals %}
> \<template v-for="mediaField in mediaFields"\>
> \<sw-media-modal-v2
> :key="mediaField.name + '-media-modal'"
> v-if="mediaField.mediaModalIsOpen"
> variant="regular"
> :caption="$tc('sw-cms.elements.general.config.caption.mediaUpload')"
> :entity-context="cmsPageState.entityName"
> :allow-multi-select="false"
> :initial-folder-id="cmsPageState.defaultMediaFolderId"
> @media-upload-remove-image="mediaField.onImageRemove"
> @media-modal-selection-change="mediaField.onSelectionChanges"
> @modal-close="mediaField.onCloseModal"
> /\>
> \</template\>
> {% endblock %}
> \</div\>
> {% endblock %}
> \`\`\`
> 
> \</details\>
> 
> \<details\>
> \<summary\>media-field.mixin.js\</summary\>
> 
> \`\`\`js
> import MediaField from './media-field'
> 
> const { Mixin } = Shopware;
> 
> Mixin.register('cms-media-field', {
> 
> inject: \['repositoryFactory'\],
> 
> computed: {
> mediaRepository() {
> return this.repositoryFactory.create('media');
> },
> },
> 
> methods: {
> createMediaField(mediaFieldName) {
> let media;
> 
> if (
> this.element?.data &&
> this.element.data\[mediaFieldName\] &&
> this.element.data\[mediaFieldName\].id
> ) {
> media = this.element.data\[mediaFieldName\];
> } else {
> media = this.element.config\[mediaFieldName\].value
> }
> 
> return new MediaField({
> name: mediaFieldName,
> parent: this,
> mediaRepository: this.mediaRepository,
> media,
> })
> }
> }
> 
> });
> \`\`\`
> 
> \</details\>
> 
> 
> \<details\>
> \<summary\>media-field.js\</summary\>
> 
> \`\`\`js
> export default class MediaField {
> constructor({
> name,
> parent,
> mediaRepository,
> media = null,
> mediaModalIsOpen = false,
> initialFolderId = null,
> }) {
> if (!name) {
> throw new Error('MediaField requires a name');
> }
> 
> if (!parent) {
> throw new Error('MediaField requires a parent');
> }
> 
> if (!mediaRepository) {
> throw new Error('MediaField requires a mediaRepository');
> }
> 
> this.name = name;
> this.parent = parent;
> this.mediaRepository = mediaRepository;
> this.media = media;
> this.mediaModalIsOpen = mediaModalIsOpen;
> this.initialFolderId = initialFolderId;
> 
> this.onImageUpload = this.onImageUploadProxy.bind(this)
> this.onImageRemove = this.onImageRemoveProxy.bind(this)
> this.onOpenMediaModal = this.onOpenMediaModalProxy.bind(this)
> this.onCloseModal = this.onCloseModalProxy.bind(this)
> this.onSelectionChanges = this.onSelectionChangesProxy.bind(this)
> }
> 
> getName() {
> return this.name;
> }
> 
> isMediaModalOpen() {
> return this.mediaModalIsOpen;
> }
> 
> setMediaModalIsOpen(mediaModalIsOpen) {
> this.mediaModalIsOpen = mediaModalIsOpen;
> }
> 
> getMedia() {
> return this.media;
> }
> 
> setMedia(media = null) {
> this.media = media;
> }
> 
> getInitialFolderId() {
> return this.initialFolderId;
> }
> 
> setInitialFolderId(initialFolderId) {
> this.initialFolderId = initialFolderId;
> }
> 
> getUploadTag() {
> return \`cms-element-media-config-${this.parent.element.id}-${this.name}\`;
> }
> 
> async onImageUploadProxy({ targetId }) {
> const mediaEntity = await this.mediaRepository.get(targetId);
> 
> this.updateParentElementConfigValue(mediaEntity.id);
> this.setMedia(mediaEntity);
> 
> this.updateParentElementData(mediaEntity);
> 
> this.updateParent()
> }
> 
> onImageRemoveProxy() {
> this.updateParentElementConfigValue(null);
> this.setMedia(null);
> 
> this.updateParentElementData();
> 
> this.updateParent();
> }
> 
> onOpenMediaModalProxy() {
> this.setMediaModalIsOpen(true);
> }
> 
> onCloseModalProxy() {
> this.setMediaModalIsOpen(false);
> }
> 
> onSelectionChangesProxy(mediaEntity) {
> const media = mediaEntity\[0\];
> this.updateParentElementConfigValue(media.id);
> this.setMedia(media);
> 
> this.updateParentElementData(media);
> 
> this.updateParent();
> }
> 
> updateParentElementData(media = null) {
> if (!this.parent.element.data) {
> this.parent.$set(this.parent.element, 'data', {
> \[this.name\]: media
> });
> } else {
> this.parent.$set(this.parent.element.data, this.name, media);
> }
> }
> 
> updateParentElementConfigValue(mediaId) {
> this.parent.element.config\[this.name\].value = mediaId;
> }
> 
> updateParent() {
> this.parent.$emit('element-update', this.parent.element);
> }
> }
> \`\`\`
> 
> \</details\>

Das war für mich die Lösung. Anfangs hat er das mixin nicht gefunden, wenn ich es in der main.js einbinde. Erst als ich es in der /config/index.js imported habe, hat er es gefunden.

---

<div class="post-metadata">

### Author: ![leo.haerdle](https://avatars.discourse-cdn.com/v4/letter/l/8edcca/32.png) [@leo.haerdle](https://forum.shopware.com/u/leo.haerdle)
#### Post date: [28. März 2023 um 15:59 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149/4 "2023-03-28T15:59:44Z")

</div>

Ich versuche mich hier gerade auf ähnliche Weise an einem Image-Upload, habe die wesentlichen Dinge übernommen vom image Cms Element von SW. Ich bekomme aber das data Attribut nicht befüllt. Das data Attribut bleibt bei mir null. Hat jemand einen Hint wie ich hier weiterkomme?

Das einzige was ich finde ist das hier:  
`this.initElementData('image');`  
Und mir ist noch nicht so ganz klar was hier passiert.  
Wovon hängt ab ob data gefüllt ist und wie wird es gefüllt?

---

<div class="post-metadata">

### Author: ![leo.haerdle](https://avatars.discourse-cdn.com/v4/letter/l/8edcca/32.png) [@leo.haerdle](https://forum.shopware.com/u/leo.haerdle)
#### Post date: [28. März 2023 um 16:20 UTC](https://forum.shopware.com/t/cms-element-mehrere-media-uploads-wie-upload-listener-und-media-modal/96149/5 "2023-03-28T16:20:10Z")

</div>

Ok, durch diesen Thread bin ich einen Schritt weiter: [https://forum.shopware.com/t/bild-wird-in-cms-element-in-der-administration-nicht-richtig-angezeigt/71270](https://forum.shopware.com/t/bild-wird-in-cms-element-in-der-administration-nicht-richtig-angezeigt/71270)

Aber brauche ich wirklich einen eigenen DataResolver. Für sowas einfaches wie einen Image-Upload müsste es doch was geben oder? Kann ich das irgendwie analog machen wie das image Cms Element von SW?

Aber wie und wo passiert das?
