How to update other files type field from Vue component Javascript

Hi all,

I’m building a plugin that will use one extended files field to load an “big” image and resize it. After resize, I want to update other page fields with new resized image.
I’m stuck in Javascript updating other files type fields (targetfields var is a prop from my custom field that specifies list of fields name that I want to update and other props like width/height):

<script>
export default {
  extends: 'k-files-field',
  props: {
    targetfields: {
      type: Array,
      default: () => []
    },
  },
  methods: {
    onInput(value) {
      var pageid = this.$store.state.content.current;
      var targetfield = this.targetfields[0]['name'];
      //var fld = this.$store.getters['content/values'](pageid)[targetfield];
      let newimage = this.$refs.fileUpload.files[0]);

      this.$store.commit('content/update', {
        id: pageid,
        field: targetfield,
        value: newimage
      });
    }
  }
}

Panel look and feel (“Big image” field upload will update also the other fields):

To simplify the question: Actually, I need a way to update other fields (file type) from current component.
Something like:

this.$store.dispatch('content/update',.....

Maybe this helps: Programmatically change the content of the file field - #38 by bastianallgeier

May I ask why you actually need all these fields, given that you can create all those thumbs in the frontend on the fly?

Thank you for your response. I already visited that link and still not clear.

The flow should be like this:

  • upload a “big” picture → resize/process the image (server side or client side)
  • prefill the other fields with new created images (this is what I cannot achieve server side because the frontend fields are empty and will override their content on Save)
  • user can change image from other fields if needed (automatic processing failed to deliver desired use cases)

Unfortunately, I cannot help you with this any further.

What is supposed to happen if the user manually selects some images in those other fields and then uploads a new big image? Would that newly processed smaller images then override the already selected ones? Just trying to think about a workaround…

No worries with the questions.

All the time will override:

  • If the user will update the big image, then everything will be overridden.
  • If the user will update the small image(s), then will specifically override that field.

The bigimage field is a custom field only. The small ones are default kirby file fields.

Kirby::plugin('raduhoria/autoresizefiles', [
    'fields' => [
        'fileswithresize' => [
            'extends' => 'files',
            'props' => [
                'targetfields' => function (array $targetfields = null) {
                    return $targetfields;
                }
            ],
        ],
    ],

and blueprint:

      bigimage:
        type: fileswithresize
        label: Big Image
        max: 1
        multiple: false
        targetfields: [img178pxX240px, img220pxX290px]
      img178pxX240px:
        type: files
        label: "178px X 240px:"
        max: 1
        multiple: false
      img220pxX290px:
        type: files
        label: "220px X 290px:"
        max: 1
        multiple: false

I manage to workaround a solution without JavaScript or frontend intervention.

First, I created a hook and filter the call of resize function by page template name. This hook will be triggered after the “bigimage” ($file parameter) will be uploaded.

    'hooks' => [
        'file.create:after' => function ($file) {
            if($file->parent()->template()->name()=='game') {
                autoResizeImage($file);
            }
        },
    ],

Now, the autoResizeImage function is based on the blueprint field details that is a custom field (type: fileswithresize) which will be described like this (notice the targetfields value which will address the existing target fields from the blueprint):

fields:
  bigimage:
    type: fileswithresize
    label: Big Image
    max: 1
    multiple: false
    targetfields:
      - name: img178pxX240px
        width: 178
        height: 240
      - name: img220pxX290px
        width: 220
        height: 290
  images:
      label: Images
      icon: image
      fields:
        img178pxX240px:
          type: files
          label: "178px X 240px:"
          max: 1
          multiple: false
        img220pxX290px:
          type: files
          label: "220px X 290px:"
          max: 1
          multiple: false

autoResizeImage function:

function autoResizeImage($file){
    $default_lang = kirby()->defaultLanguage()->code();
    $current_page = $file->parent();
    $page_template_name = $current_page->template()->name();
    $blueprint = 'pages/'.$page_template_name;
    $blueprintfields = Blueprint::factory($blueprint,null,page())->fields();
    $pagecutom_fields_attributes = array_column($blueprintfields,'targetfields'); // can be more custom fields on the page
    deleteImages($current_page,$file);
    if($file->isResizable()){
        if(is_array($pagecutom_fields_attributes[0])){
            $fields_to_be_updated = [];
            foreach ($pagecutom_fields_attributes[0] as $attribute) {
                $targetfield = $attribute['name'];
                $width = $attribute['width'];
                $height = $attribute['height'];
                $quality = 100;
                try {
                    $newfile = $file->crop($width, $height, $quality);
                    $newfile_created = $current_page->createFile([
                        'source'   => $newfile->root(),
                        'parent' => $current_page,
                        'filename' => $newfile->filename(),
                        'template' => 'default'
                    ], true);
                    $newfile_created->update();
                    $fields_to_be_updated[$attribute['name']] = Yaml::encode($newfile_created->filename());
                }catch (Exception $e) {
                    throw new Exception($e->getMessage());
                }
            }
            $fields_to_be_updated['update_type'] = 'autoresize';
            if(count($fields_to_be_updated)>0) {
                $current_page->update($fields_to_be_updated, $default_lang);
            }
        }
    }
}

function deleteImages($page, $file_except = null){
    $files = $page->files();
    foreach ($files as $file) {
        if($file->filename() != $file_except->filename()) {
            $file->delete();
        }
    }
}

Now, if you do the above, the target fields will be updated but not saved yet and when trying to save the data files will be overridden with empty data. In order to avoid this, I had to get and save the data with state pushed from the autoResizeImage function → line:

$fields_to_be_updated[‘update_type’] = ‘autoresize’;

into the writeContent method from the page (which is a virtual page also).

 public function writeContent(array $data, string $languageCode = null): bool
{
//this should be on update only
                if (isset($data['update_type'])){
                    switch ($data['update_type']) {
                        case 'autoresize':
                            $data['update_type'] = 'autoresize_save'; //reset the update type to status for saving the page
                            break;
                        case 'autoresize_save':
                            $blueprint = 'pages/game';
                            $blueprintfields = Blueprint::factory($blueprint,null,page())->fields(); // get the fields from blueprint
                            $pagecutom_fields_attributes = array_column($blueprintfields,'targetfields'); //filter the custom fields
                            if(is_array($pagecutom_fields_attributes[0])){ // only one custom field permited per page
                                foreach ($pagecutom_fields_attributes[0] as $attribute) { // get attributes from custom field
                                    if(!empty($this->{$attribute['name']}()->value())){ // preserve already saved field value
                                        $data[$attribute['name']] = $this->{$attribute['name']}()->value(); // override the image field with the existing value
                                    }
                                }
                            }
                            $data['update_type'] = 'normal'; //reset to normal
                            break;
                        case 'normal':
                            // do nothing, let the image be saved as is
                            break;
                    }
                }
}

Also, to ensure two steps (refresh the page after update) I created two tabs, one for the big image and one for the target fields. Also, I used an section gallery files to give the user a preview after upload and creating of the new files (which will be great if someone will explain me how to make it read-only).

First step:

Second tab with target images fields with correct data and with full functionality (like replace it manually if wanted):

That’s it.