AlexBS
17. September 2022 um 12:07
3
opened 03:50PM - 13 Apr 22 UTC
Feature Request
### 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.
1 „Gefällt mir“